mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
feat: support select field as tool output (#2798)
* feat: support select field as tool output * defaultOutput
This commit is contained in:
@@ -25,13 +25,11 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { isWorkflowStartOutput } from '@fastgpt/global/core/workflow/template/system/workflowStart';
|
||||
import { defaultInput } from '../render/RenderInput/FieldEditModal';
|
||||
import PluginOutputEditModal, { defaultOutput } from './PluginOutputEditModal';
|
||||
|
||||
const FieldEditModal = dynamic(() => import('../render/RenderInput/FieldEditModal'));
|
||||
|
||||
const customInputConfig = {
|
||||
const customOutputConfig = {
|
||||
selectValueTypeList: Object.values(WorkflowIOValueTypeEnum),
|
||||
showDescription: true,
|
||||
|
||||
showDefaultValue: true
|
||||
};
|
||||
|
||||
@@ -62,7 +60,7 @@ const NodePluginOutput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
onClick={() => setEditField(defaultInput)}
|
||||
onClick={() => setEditField(defaultOutput)}
|
||||
>
|
||||
{t('common:common.Add New')}
|
||||
</Button>
|
||||
@@ -78,9 +76,9 @@ const NodePluginOutput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
</Box>
|
||||
</Container>
|
||||
{!!editField && (
|
||||
<FieldEditModal
|
||||
customInputConfig={customInputConfig}
|
||||
defaultInput={editField}
|
||||
<PluginOutputEditModal
|
||||
customOutputConfig={customOutputConfig}
|
||||
defaultOutput={editField}
|
||||
keys={inputs.map((input) => input.key)}
|
||||
onClose={() => setEditField(undefined)}
|
||||
onSubmit={({ data }) => {
|
||||
@@ -171,31 +169,46 @@ function Reference({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex alignItems={'center'} mb={1}>
|
||||
<FormLabel required={input.required}>{input.label}</FormLabel>
|
||||
{input.description && <QuestionTip ml={0.5} label={input.description}></QuestionTip>}
|
||||
{/* value */}
|
||||
<ValueTypeLabel valueType={input.valueType} valueDesc={input.valueDesc} />
|
||||
<Flex alignItems={'center'} justify={'space-between'} mb={1}>
|
||||
<Flex>
|
||||
<FormLabel required={input.required}>{input.label}</FormLabel>
|
||||
{input.description && <QuestionTip ml={0.5} label={input.description}></QuestionTip>}
|
||||
{/* value */}
|
||||
<ValueTypeLabel valueType={input.valueType} valueDesc={input.valueDesc} />
|
||||
|
||||
<MyIcon
|
||||
name={'common/settingLight'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
ml={3}
|
||||
color={'myGray.600'}
|
||||
_hover={{ color: 'primary.500' }}
|
||||
onClick={() => setEditField(input)}
|
||||
/>
|
||||
<MyIcon
|
||||
name={'common/settingLight'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
ml={3}
|
||||
color={'myGray.600'}
|
||||
_hover={{ color: 'primary.500' }}
|
||||
onClick={() => setEditField(input)}
|
||||
/>
|
||||
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
color={'myGray.500'}
|
||||
cursor={'pointer'}
|
||||
ml={2}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={openConfirm(onDel)}
|
||||
/>
|
||||
</Flex>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
name={
|
||||
input.isToolOutput !== false
|
||||
? 'core/workflow/template/toolkitActive'
|
||||
: 'core/workflow/template/toolkitInactive'
|
||||
}
|
||||
w={'14px'}
|
||||
color={'myGray.500'}
|
||||
cursor={'pointer'}
|
||||
ml={2}
|
||||
mr={2}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={openConfirm(onDel)}
|
||||
onClick={() => setEditField(input)}
|
||||
/>
|
||||
</Flex>
|
||||
<ReferSelector
|
||||
@@ -206,9 +219,9 @@ function Reference({
|
||||
/>
|
||||
|
||||
{!!editField && (
|
||||
<FieldEditModal
|
||||
defaultInput={editField}
|
||||
customInputConfig={customInputConfig}
|
||||
<PluginOutputEditModal
|
||||
defaultOutput={editField}
|
||||
customOutputConfig={customOutputConfig}
|
||||
keys={keys}
|
||||
onClose={() => setEditField(undefined)}
|
||||
onSubmit={onUpdateField}
|
||||
|
@@ -0,0 +1,197 @@
|
||||
import { FlowValueTypeMap } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
Input,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Stack,
|
||||
Textarea,
|
||||
Switch
|
||||
} from '@chakra-ui/react';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import {
|
||||
CustomFieldConfigType,
|
||||
FlowNodeInputItemType
|
||||
} from '@fastgpt/global/core/workflow/type/io';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useMount } from 'ahooks';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
|
||||
const PluginOutputEditModal = ({
|
||||
customOutputConfig,
|
||||
defaultOutput,
|
||||
keys,
|
||||
onClose,
|
||||
onSubmit
|
||||
}: {
|
||||
customOutputConfig: CustomFieldConfigType;
|
||||
defaultOutput: FlowNodeInputItemType;
|
||||
keys: string[];
|
||||
onClose: () => void;
|
||||
onSubmit: (e: { data: FlowNodeInputItemType; isChangeKey: boolean }) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const isEdit = !!defaultOutput.key;
|
||||
|
||||
const { register, setValue, handleSubmit, watch } = useForm<FlowNodeInputItemType>({
|
||||
defaultValues: { ...defaultOutput, isToolOutput: defaultOutput.isToolOutput !== false }
|
||||
});
|
||||
const inputType = FlowNodeInputTypeEnum.reference;
|
||||
|
||||
// value type select
|
||||
const showValueTypeSelect = useMemo(() => {
|
||||
if (
|
||||
!customOutputConfig.selectValueTypeList ||
|
||||
customOutputConfig.selectValueTypeList.length <= 1
|
||||
)
|
||||
return false;
|
||||
if (inputType === FlowNodeInputTypeEnum.reference) return true;
|
||||
|
||||
return false;
|
||||
}, [customOutputConfig.selectValueTypeList, inputType]);
|
||||
const valueTypeSelectList = useMemo(() => {
|
||||
if (!customOutputConfig.selectValueTypeList) return [];
|
||||
|
||||
const dataTypeSelectList = Object.values(FlowValueTypeMap).map((item) => ({
|
||||
label: t(item.label as any),
|
||||
value: item.value
|
||||
}));
|
||||
|
||||
return dataTypeSelectList.filter((item) =>
|
||||
customOutputConfig.selectValueTypeList?.includes(item.value)
|
||||
);
|
||||
}, [customOutputConfig.selectValueTypeList, t]);
|
||||
|
||||
const valueType = watch('valueType');
|
||||
|
||||
useMount(() => {
|
||||
if (
|
||||
customOutputConfig.selectValueTypeList &&
|
||||
customOutputConfig.selectValueTypeList.length > 0 &&
|
||||
!valueType
|
||||
) {
|
||||
setValue('valueType', customOutputConfig.selectValueTypeList[0]);
|
||||
}
|
||||
});
|
||||
|
||||
const onSubmitSuccess = useCallback(
|
||||
(data: FlowNodeInputItemType) => {
|
||||
const isChangeKey = defaultOutput.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?.key?.trim();
|
||||
data.label = data.key;
|
||||
|
||||
onSubmit({
|
||||
data,
|
||||
isChangeKey
|
||||
});
|
||||
onClose();
|
||||
},
|
||||
[defaultOutput.key, isEdit, keys, onClose, onSubmit, toast, t]
|
||||
);
|
||||
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}
|
||||
iconSrc="core/workflow/template/pluginOutput"
|
||||
title={isEdit ? t('workflow:edit_output') : t('workflow:add_new_output')}
|
||||
overflow={'unset'}
|
||||
>
|
||||
<ModalBody w={'100%'} overflow={'auto'} display={'flex'} flexDirection={['column', 'row']}>
|
||||
<Stack w={'100%'} spacing={3}>
|
||||
{showValueTypeSelect && (
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 70px'}>{t('common:core.module.Data Type')}</FormLabel>
|
||||
<Box flex={1}>
|
||||
<MySelect<WorkflowIOValueTypeEnum>
|
||||
w={'full'}
|
||||
list={valueTypeSelectList.filter(
|
||||
(item) => item.value !== WorkflowIOValueTypeEnum.arrayAny
|
||||
)}
|
||||
value={valueType}
|
||||
onchange={(e) => {
|
||||
setValue('valueType', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{/* key */}
|
||||
<Flex mt={3} alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 70px'} required>
|
||||
{t('common:core.module.Field Name')}
|
||||
</FormLabel>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
placeholder="appointment/sql"
|
||||
{...register('key', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex mt={3} alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 70px'}>{t('workflow:input_description')}</FormLabel>
|
||||
<Textarea bg={'myGray.50'} {...register('description', {})} />
|
||||
</Flex>
|
||||
<Flex mt={3} alignItems={'center'} justifyContent={'space-between'}>
|
||||
<FormLabel>{t('workflow:is_tool_output')}</FormLabel>
|
||||
<Switch {...register('isToolOutput')} />
|
||||
</Flex>
|
||||
</Stack>
|
||||
</ModalBody>
|
||||
<ModalFooter gap={3}>
|
||||
<Button variant={'whiteBase'} onClick={onClose}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button onClick={handleSubmit(onSubmitSuccess, onSubmitError)}>
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default PluginOutputEditModal;
|
||||
|
||||
export const defaultOutput: FlowNodeInputItemType = {
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference],
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
canEdit: true,
|
||||
key: '',
|
||||
label: '',
|
||||
isToolOutput: true
|
||||
};
|
Reference in New Issue
Block a user