mirror of
https://github.com/labring/FastGPT.git
synced 2025-08-02 20:58:12 +00:00
feat: http modules
This commit is contained in:
@@ -3,9 +3,9 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase, App } from '@/service/mongo';
|
||||
import { AppModuleInputItemType } from '@/types/app';
|
||||
import { FlowModuleTypeEnum, SpecialInputKeyEnum } from '@/constants/flow';
|
||||
import { TaskResponseKeyEnum } from '@/constants/chat';
|
||||
import { FlowInputItemType } from '@/types/flow';
|
||||
|
||||
const chatModelInput = ({
|
||||
model,
|
||||
@@ -21,7 +21,7 @@ const chatModelInput = ({
|
||||
systemPrompt: string;
|
||||
limitPrompt: string;
|
||||
kbList: { kbId: string }[];
|
||||
}): AppModuleInputItemType[] => [
|
||||
}): FlowInputItemType[] => [
|
||||
{
|
||||
key: 'model',
|
||||
value: model,
|
||||
|
@@ -27,7 +27,8 @@ const NodeCQNode = ({
|
||||
CustomComponent={{
|
||||
[SpecialInputKeyEnum.agents]: ({
|
||||
key: agentKey,
|
||||
value: agents = []
|
||||
value: agents = [],
|
||||
...props
|
||||
}: {
|
||||
key: string;
|
||||
value?: ClassifyQuestionAgentItemType[];
|
||||
@@ -50,7 +51,11 @@ const NodeCQNode = ({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: agentKey,
|
||||
value: newInputValue
|
||||
value: {
|
||||
...props,
|
||||
key: agentKey,
|
||||
value: newInputValue
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
@@ -77,8 +82,13 @@ const NodeCQNode = ({
|
||||
);
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: agentKey,
|
||||
value: newVal
|
||||
value: {
|
||||
...props,
|
||||
key: agentKey,
|
||||
value: newVal
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -97,11 +107,16 @@ const NodeCQNode = ({
|
||||
type: FlowOutputItemTypeEnum.hidden,
|
||||
targets: []
|
||||
});
|
||||
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: agentKey,
|
||||
value: newInputValue
|
||||
value: {
|
||||
...props,
|
||||
key: agentKey,
|
||||
value: newInputValue
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
|
@@ -48,33 +48,32 @@ const NodeChat = ({
|
||||
onchange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: inputItem.key,
|
||||
value: e
|
||||
value: {
|
||||
...inputItem,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
|
||||
// update max tokens
|
||||
const model = chatModelList.find((item) => item.model === e);
|
||||
const model =
|
||||
chatModelList.find((item) => item.model === e) || chatModelList[0];
|
||||
if (!model) return;
|
||||
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: 'maxToken',
|
||||
valueKey: 'markList',
|
||||
value: [
|
||||
{ label: '100', value: 100 },
|
||||
{ label: `${model.contextMaxToken}`, value: model.contextMaxToken }
|
||||
]
|
||||
});
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
key: 'maxToken',
|
||||
valueKey: 'max',
|
||||
value: model.contextMaxToken
|
||||
});
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
key: 'maxToken',
|
||||
valueKey: 'value',
|
||||
value: model.contextMaxToken / 2
|
||||
value: {
|
||||
...inputs.find((input) => input.key === 'maxToken'),
|
||||
markList: [
|
||||
{ label: '100', value: 100 },
|
||||
{ label: `${model.contextMaxToken}`, value: model.contextMaxToken }
|
||||
],
|
||||
max: model.contextMaxToken,
|
||||
value: model.contextMaxToken / 2
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -100,8 +99,12 @@ const NodeChat = ({
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: inputItem.key,
|
||||
value: e
|
||||
value: {
|
||||
...inputItem,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -115,7 +118,11 @@ const NodeChat = ({
|
||||
<>
|
||||
<Divider text="Output" />
|
||||
<Container>
|
||||
<RenderOutput flowOutputList={outputs} />
|
||||
<RenderOutput
|
||||
onChangeNode={onChangeNode}
|
||||
moduleId={moduleId}
|
||||
flowOutputList={outputs}
|
||||
/>
|
||||
</Container>
|
||||
</>
|
||||
)}
|
||||
|
@@ -31,10 +31,9 @@ const NodeExtract = ({
|
||||
flowInputList={inputs}
|
||||
CustomComponent={{
|
||||
[ContextExtractEnum.extractKeys]: ({
|
||||
key,
|
||||
value: extractKeys = []
|
||||
value: extractKeys = [],
|
||||
...props
|
||||
}: {
|
||||
key: string;
|
||||
value?: ContextExtractAgentItemType[];
|
||||
}) => (
|
||||
<Box pt={2}>
|
||||
@@ -97,7 +96,10 @@ const NodeExtract = ({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: ContextExtractEnum.extractKeys,
|
||||
value: newInputValue
|
||||
value: {
|
||||
...props,
|
||||
value: newInputValue
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
@@ -121,7 +123,7 @@ const NodeExtract = ({
|
||||
</Container>
|
||||
<Divider text="Output" />
|
||||
<Container>
|
||||
<RenderOutput flowOutputList={outputs} />
|
||||
<RenderOutput onChangeNode={onChangeNode} moduleId={moduleId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
|
||||
{!!editExtractFiled && (
|
||||
@@ -160,7 +162,10 @@ const NodeExtract = ({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: ContextExtractEnum.extractKeys,
|
||||
value: newInputs
|
||||
value: {
|
||||
...inputs.find((input) => input.key === ContextExtractEnum.extractKeys),
|
||||
value: newInputs
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
|
@@ -18,7 +18,11 @@ const NodeHistory = ({
|
||||
</Container>
|
||||
<Divider text="Output" />
|
||||
<Container>
|
||||
<RenderOutput flowOutputList={outputs} />
|
||||
<RenderOutput
|
||||
onChangeNode={onChangeNode}
|
||||
moduleId={props.moduleId}
|
||||
flowOutputList={outputs}
|
||||
/>
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
|
@@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import NodeCard from '../modules/NodeCard';
|
||||
import { FlowModuleItemType } from '@/types/flow';
|
||||
import Divider from '../modules/Divider';
|
||||
import Container from '../modules/Container';
|
||||
import RenderInput from '../render/RenderInput';
|
||||
import { Box, Button } from '@chakra-ui/react';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import RenderOutput from '../render/RenderOutput';
|
||||
|
||||
import { FlowInputItemTypeEnum, FlowOutputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
|
||||
const NodeHttp = ({
|
||||
data: { moduleId, inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
return (
|
||||
<NodeCard minW={'350px'} moduleId={moduleId} {...props}>
|
||||
<Container borderTop={'2px solid'} borderTopColor={'myGray.200'}>
|
||||
<RenderInput moduleId={moduleId} onChangeNode={onChangeNode} flowInputList={inputs} />
|
||||
<Button
|
||||
variant={'base'}
|
||||
mt={5}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
onClick={() => {
|
||||
const key = nanoid();
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'addInput',
|
||||
key,
|
||||
value: {
|
||||
key,
|
||||
value: '',
|
||||
valueType: FlowValueTypeEnum.string,
|
||||
type: FlowInputItemTypeEnum.target,
|
||||
label: 'New Param',
|
||||
edit: true
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
添加入参
|
||||
</Button>
|
||||
</Container>
|
||||
<Divider text="Output" />
|
||||
<Container>
|
||||
<RenderOutput onChangeNode={onChangeNode} moduleId={moduleId} flowOutputList={outputs} />
|
||||
<Box textAlign={'right'} mt={5}>
|
||||
<Button
|
||||
variant={'base'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
onClick={() => {
|
||||
const key = nanoid();
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'outputs',
|
||||
key,
|
||||
value: outputs.concat([
|
||||
{
|
||||
key,
|
||||
label: '出参1',
|
||||
valueType: FlowValueTypeEnum.string,
|
||||
type: FlowOutputItemTypeEnum.source,
|
||||
edit: true,
|
||||
targets: []
|
||||
}
|
||||
])
|
||||
});
|
||||
}}
|
||||
>
|
||||
添加出参
|
||||
</Button>
|
||||
</Box>
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
};
|
||||
export default React.memo(NodeHttp);
|
@@ -81,14 +81,19 @@ const NodeKbSearch = ({
|
||||
onChangeNode={onChangeNode}
|
||||
flowInputList={inputs}
|
||||
CustomComponent={{
|
||||
kbList: ({ key, value }) => (
|
||||
kbList: ({ key, value, ...props }) => (
|
||||
<KBSelect
|
||||
activeKbs={value}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
key,
|
||||
value: e
|
||||
type: 'inputs',
|
||||
value: {
|
||||
...props,
|
||||
key,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -98,7 +103,7 @@ const NodeKbSearch = ({
|
||||
</Container>
|
||||
<Divider text="Output" />
|
||||
<Container>
|
||||
<RenderOutput flowOutputList={outputs} />
|
||||
<RenderOutput onChangeNode={onChangeNode} moduleId={moduleId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
|
@@ -9,7 +9,7 @@ import { FlowValueTypeEnum } from '@/constants/flow';
|
||||
import SourceHandle from '../render/SourceHandle';
|
||||
|
||||
const QuestionInputNode = ({
|
||||
data: { inputs, outputs, onChangeNode, ...props }
|
||||
data: { inputs, outputs, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
return (
|
||||
<NodeCard minW={'240px'} {...props}>
|
||||
|
@@ -14,7 +14,7 @@ const NodeUserGuide = ({
|
||||
data: { inputs, outputs, onChangeNode, ...props }
|
||||
}: NodeProps<FlowModuleItemType>) => {
|
||||
const welcomeText = useMemo(
|
||||
() => inputs.find((item) => item.key === SystemInputEnum.welcomeText)?.value,
|
||||
() => inputs.find((item) => item.key === SystemInputEnum.welcomeText),
|
||||
[inputs]
|
||||
);
|
||||
|
||||
@@ -30,22 +30,27 @@ const NodeUserGuide = ({
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
<Textarea
|
||||
className="nodrag"
|
||||
rows={6}
|
||||
resize={'both'}
|
||||
defaultValue={welcomeText}
|
||||
bg={'myWhite.500'}
|
||||
placeholder={welcomeTextTip}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId: props.moduleId,
|
||||
key: SystemInputEnum.welcomeText,
|
||||
type: 'inputs',
|
||||
value: e.target.value
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{welcomeText && (
|
||||
<Textarea
|
||||
className="nodrag"
|
||||
rows={6}
|
||||
resize={'both'}
|
||||
defaultValue={welcomeText.value}
|
||||
bg={'myWhite.500'}
|
||||
placeholder={welcomeTextTip}
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId: props.moduleId,
|
||||
key: SystemInputEnum.welcomeText,
|
||||
type: 'inputs',
|
||||
value: {
|
||||
...welcomeText,
|
||||
value: e.target.value
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</Container>
|
||||
</NodeCard>
|
||||
|
@@ -40,10 +40,13 @@ const NodeUserGuide = ({
|
||||
moduleId: props.moduleId,
|
||||
key: SystemInputEnum.variables,
|
||||
type: 'inputs',
|
||||
value
|
||||
value: {
|
||||
...inputs.find((item) => item.key === SystemInputEnum.variables),
|
||||
value
|
||||
}
|
||||
});
|
||||
},
|
||||
[onChangeNode, props.moduleId]
|
||||
[inputs, onChangeNode, props.moduleId]
|
||||
);
|
||||
|
||||
const onclickSubmit = useCallback(
|
||||
|
@@ -0,0 +1,116 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ModalHeader,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
Flex,
|
||||
Switch,
|
||||
Input,
|
||||
FormControl
|
||||
} from '@chakra-ui/react';
|
||||
import type { ContextExtractAgentItemType, HttpFieldItemType } from '@/types/app';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
import MyModal from '@/components/MyModal';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { FlowInputItemTypeEnum, FlowValueTypeEnum, FlowValueTypeStyle } from '@/constants/flow';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import MySelect from '@/components/Select';
|
||||
import { FlowInputItemType } from '@/types/flow';
|
||||
|
||||
const typeSelectList = [
|
||||
{
|
||||
label: '字符串',
|
||||
value: FlowValueTypeEnum.string
|
||||
},
|
||||
{
|
||||
label: '数字',
|
||||
value: FlowValueTypeEnum.number
|
||||
},
|
||||
{
|
||||
label: '布尔',
|
||||
value: FlowValueTypeEnum.boolean
|
||||
},
|
||||
{
|
||||
label: '任意',
|
||||
value: FlowValueTypeEnum.any
|
||||
}
|
||||
];
|
||||
|
||||
const SetInputFieldModal = ({
|
||||
defaultField = {
|
||||
label: '',
|
||||
key: '',
|
||||
type: FlowInputItemTypeEnum.target,
|
||||
valueType: FlowValueTypeEnum.string,
|
||||
description: '',
|
||||
required: false
|
||||
},
|
||||
onClose,
|
||||
onSubmit
|
||||
}: {
|
||||
defaultField?: FlowInputItemType;
|
||||
onClose: () => void;
|
||||
onSubmit: (data: FlowInputItemType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { register, getValues, setValue, handleSubmit } = useForm<FlowInputItemType>({
|
||||
defaultValues: defaultField
|
||||
});
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
return (
|
||||
<MyModal isOpen={true} onClose={onClose}>
|
||||
<ModalHeader display={'flex'} alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/extract.png'} mr={2} w={'20px'} objectFit={'cover'} />
|
||||
{t('app.Input Field Settings')}
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box flex={'0 0 70px'}>必填</Box>
|
||||
<Switch {...register('required')} />
|
||||
</Flex>
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<Box flex={'0 0 70px'}>字段类型</Box>
|
||||
<MySelect
|
||||
w={'288px'}
|
||||
list={typeSelectList}
|
||||
value={getValues('valueType')}
|
||||
onchange={(e: any) => {
|
||||
setValue('valueType', e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<Box flex={'0 0 70px'}>字段名</Box>
|
||||
<Input
|
||||
placeholder="预约字段/sql语句……"
|
||||
{...register('label', { required: '字段名不能为空' })}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<Box flex={'0 0 70px'}>字段 key</Box>
|
||||
<Input
|
||||
placeholder="appointment/sql"
|
||||
{...register('key', { required: '字段 key 不能为空' })}
|
||||
/>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'base'} mr={3} onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleSubmit(onSubmit)}>确认</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SetInputFieldModal);
|
@@ -0,0 +1,105 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ModalHeader,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
Flex,
|
||||
Switch,
|
||||
Input,
|
||||
FormControl
|
||||
} from '@chakra-ui/react';
|
||||
import type { ContextExtractAgentItemType, HttpFieldItemType } from '@/types/app';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
|
||||
import MyModal from '@/components/MyModal';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { FlowOutputItemTypeEnum, FlowValueTypeEnum, FlowValueTypeStyle } from '@/constants/flow';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import MySelect from '@/components/Select';
|
||||
import { FlowOutputItemType } from '@/types/flow';
|
||||
|
||||
const typeSelectList = [
|
||||
{
|
||||
label: '字符串',
|
||||
value: FlowValueTypeEnum.string
|
||||
},
|
||||
{
|
||||
label: '数字',
|
||||
value: FlowValueTypeEnum.number
|
||||
},
|
||||
{
|
||||
label: '布尔',
|
||||
value: FlowValueTypeEnum.boolean
|
||||
},
|
||||
{
|
||||
label: '任意',
|
||||
value: FlowValueTypeEnum.any
|
||||
}
|
||||
];
|
||||
|
||||
const SetInputFieldModal = ({
|
||||
defaultField,
|
||||
onClose,
|
||||
onSubmit
|
||||
}: {
|
||||
defaultField: FlowOutputItemType;
|
||||
onClose: () => void;
|
||||
onSubmit: (data: FlowOutputItemType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { register, getValues, setValue, handleSubmit } = useForm<FlowOutputItemType>({
|
||||
defaultValues: defaultField
|
||||
});
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
|
||||
return (
|
||||
<MyModal isOpen={true} onClose={onClose}>
|
||||
<ModalHeader display={'flex'} alignItems={'center'}>
|
||||
<Avatar src={'/imgs/module/extract.png'} mr={2} w={'20px'} objectFit={'cover'} />
|
||||
{t('app.Output Field Settings')}
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<Box flex={'0 0 70px'}>字段类型</Box>
|
||||
<MySelect
|
||||
w={'288px'}
|
||||
list={typeSelectList}
|
||||
value={getValues('valueType')}
|
||||
onchange={(e: any) => {
|
||||
setValue('valueType', e);
|
||||
setRefresh(!refresh);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<Box flex={'0 0 70px'}>字段名</Box>
|
||||
<Input
|
||||
placeholder="预约字段/sql语句……"
|
||||
{...register('label', { required: '字段名不能为空' })}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex mt={5} alignItems={'center'}>
|
||||
<Box flex={'0 0 70px'}>字段 key</Box>
|
||||
<Input
|
||||
placeholder="appointment/sql"
|
||||
{...register('key', { required: '字段 key 不能为空' })}
|
||||
/>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button variant={'base'} mr={3} onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleSubmit(onSubmit)}>确认</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SetInputFieldModal);
|
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import type { FlowInputItemType, FlowModuleItemType } from '@/types/flow';
|
||||
import {
|
||||
Box,
|
||||
@@ -8,38 +8,129 @@ import {
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberDecrementStepper
|
||||
NumberDecrementStepper,
|
||||
Flex
|
||||
} from '@chakra-ui/react';
|
||||
import { FlowInputItemTypeEnum } from '@/constants/flow';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MySelect from '@/components/Select';
|
||||
import MySlider from '@/components/Slider';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import TargetHandle from './TargetHandle';
|
||||
import MyIcon from '@/components/Icon';
|
||||
const SetInputFieldModal = dynamic(() => import('../modules/SetInputFieldModal'));
|
||||
|
||||
export const Label = ({
|
||||
required = false,
|
||||
children,
|
||||
description
|
||||
}: {
|
||||
required?: boolean;
|
||||
children: React.ReactNode | string;
|
||||
description?: string;
|
||||
}) => (
|
||||
<Box as={'label'} display={'inline-block'} position={'relative'}>
|
||||
{children}
|
||||
{required && (
|
||||
<Box position={'absolute'} top={'-2px'} right={'-10px'} color={'red.500'} fontWeight={'bold'}>
|
||||
*
|
||||
moduleId,
|
||||
inputKey,
|
||||
onChangeNode,
|
||||
...item
|
||||
}: FlowInputItemType & {
|
||||
moduleId: string;
|
||||
inputKey: string;
|
||||
onChangeNode: FlowModuleItemType['onChangeNode'];
|
||||
}) => {
|
||||
const { required = false, description, edit, label, type, valueType } = item;
|
||||
const [editField, setEditField] = useState<FlowInputItemType>();
|
||||
|
||||
return (
|
||||
<Flex alignItems={'center'} position={'relative'}>
|
||||
<Box position={'relative'}>
|
||||
{label}
|
||||
{description && (
|
||||
<MyTooltip label={description} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
)}
|
||||
{required && (
|
||||
<Box
|
||||
position={'absolute'}
|
||||
top={'-2px'}
|
||||
right={'-8px'}
|
||||
color={'red.500'}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
*
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{description && (
|
||||
<MyTooltip label={description} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
|
||||
</MyTooltip>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
{(type === FlowInputItemTypeEnum.target || valueType) && (
|
||||
<TargetHandle handleKey={inputKey} valueType={valueType} />
|
||||
)}
|
||||
|
||||
{edit && (
|
||||
<>
|
||||
<MyIcon
|
||||
name={'settingLight'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
ml={3}
|
||||
_hover={{ color: 'myBlue.600' }}
|
||||
onClick={() =>
|
||||
setEditField({
|
||||
...item,
|
||||
key: inputKey
|
||||
})
|
||||
}
|
||||
/>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
ml={2}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => {
|
||||
{
|
||||
console.log(moduleId, inputKey, valueType);
|
||||
}
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'delInput',
|
||||
key: inputKey,
|
||||
value: ''
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!!editField && (
|
||||
<SetInputFieldModal
|
||||
defaultField={editField}
|
||||
onClose={() => setEditField(undefined)}
|
||||
onSubmit={(data) => {
|
||||
// same key
|
||||
if (editField.key === data.key) {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: inputKey,
|
||||
value: data
|
||||
});
|
||||
} else {
|
||||
// diff key. del and add
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'delInput',
|
||||
key: editField.key,
|
||||
value: ''
|
||||
});
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'addInput',
|
||||
key: editField.key,
|
||||
value: data
|
||||
});
|
||||
}
|
||||
setEditField(undefined);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const RenderBody = ({
|
||||
flowInputList,
|
||||
@@ -59,13 +150,12 @@ const RenderBody = ({
|
||||
item.type !== FlowInputItemTypeEnum.hidden && (
|
||||
<Box key={item.key} _notLast={{ mb: 7 }} position={'relative'}>
|
||||
{!!item.label && (
|
||||
<Label required={item.required} description={item.description}>
|
||||
{item.label}
|
||||
|
||||
{(item.type === FlowInputItemTypeEnum.target || item.valueType) && (
|
||||
<TargetHandle handleKey={item.key} valueType={item.valueType} />
|
||||
)}
|
||||
</Label>
|
||||
<Label
|
||||
moduleId={moduleId}
|
||||
onChangeNode={onChangeNode}
|
||||
inputKey={item.key}
|
||||
{...item}
|
||||
/>
|
||||
)}
|
||||
<Box mt={2} className={'nodrag'}>
|
||||
{item.type === FlowInputItemTypeEnum.numberInput && (
|
||||
@@ -76,8 +166,12 @@ const RenderBody = ({
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: item.key,
|
||||
value: Number(e)
|
||||
value: {
|
||||
...item,
|
||||
value: Number(e)
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -95,8 +189,12 @@ const RenderBody = ({
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: item.key,
|
||||
value: e.target.value
|
||||
value: {
|
||||
...item,
|
||||
value: e.target.value
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -110,8 +208,12 @@ const RenderBody = ({
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: item.key,
|
||||
value: e.target.value
|
||||
value: {
|
||||
...item,
|
||||
value: e.target.value
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -124,8 +226,12 @@ const RenderBody = ({
|
||||
onchange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: item.key,
|
||||
value: e
|
||||
value: {
|
||||
...item,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -142,8 +248,12 @@ const RenderBody = ({
|
||||
onChange={(e) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'inputs',
|
||||
key: item.key,
|
||||
value: e
|
||||
value: {
|
||||
...item,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
@@ -1,37 +1,147 @@
|
||||
import React from 'react';
|
||||
import type { FlowOutputItemType } from '@/types/flow';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import type { FlowModuleItemType, FlowOutputItemType } from '@/types/flow';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { FlowOutputItemTypeEnum } from '@/constants/flow';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { Handle, Position } from 'reactflow';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import SourceHandle from './SourceHandle';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import dynamic from 'next/dynamic';
|
||||
const SetOutputFieldModal = dynamic(() => import('../modules/SetOutputFieldModal'));
|
||||
|
||||
const Label = ({
|
||||
children,
|
||||
description
|
||||
}: {
|
||||
children: React.ReactNode | string;
|
||||
description?: string;
|
||||
}) => (
|
||||
<Flex as={'label'} justifyContent={'right'} alignItems={'center'} position={'relative'}>
|
||||
{description && (
|
||||
<MyTooltip label={description} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} mr={1} />
|
||||
</MyTooltip>
|
||||
)}
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
moduleId,
|
||||
outputKey,
|
||||
delOutputByKey,
|
||||
updateOutput,
|
||||
onChangeNode,
|
||||
addUpdateOutput,
|
||||
...item
|
||||
}: FlowOutputItemType & {
|
||||
outputKey: string;
|
||||
moduleId: string;
|
||||
delOutputByKey: (key: string) => void;
|
||||
updateOutput: (key: string, val: FlowOutputItemType) => void;
|
||||
addUpdateOutput: (val: FlowOutputItemType) => void;
|
||||
onChangeNode: FlowModuleItemType['onChangeNode'];
|
||||
}) => {
|
||||
const { label, description, edit } = item;
|
||||
const [editField, setEditField] = useState<FlowOutputItemType>();
|
||||
|
||||
return (
|
||||
<Flex as={'label'} justifyContent={'right'} alignItems={'center'} position={'relative'}>
|
||||
{edit && (
|
||||
<>
|
||||
<MyIcon
|
||||
name={'settingLight'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
mr={3}
|
||||
_hover={{ color: 'myBlue.600' }}
|
||||
onClick={() =>
|
||||
setEditField({
|
||||
...item,
|
||||
key: outputKey
|
||||
})
|
||||
}
|
||||
/>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
cursor={'pointer'}
|
||||
mr={3}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => delOutputByKey(outputKey)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{description && (
|
||||
<MyTooltip label={description} forceShow>
|
||||
<QuestionOutlineIcon display={['none', 'inline']} mr={1} />
|
||||
</MyTooltip>
|
||||
)}
|
||||
<Box>{label}</Box>
|
||||
|
||||
{!!editField && (
|
||||
<SetOutputFieldModal
|
||||
defaultField={editField}
|
||||
onClose={() => setEditField(undefined)}
|
||||
onSubmit={(data) => {
|
||||
console.log(data); // same key
|
||||
if (editField.key === data.key) {
|
||||
updateOutput(data.key, data);
|
||||
} else {
|
||||
delOutputByKey(editField.key);
|
||||
addUpdateOutput(data);
|
||||
}
|
||||
setEditField(undefined);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const RenderOutput = ({
|
||||
moduleId,
|
||||
flowOutputList,
|
||||
onChangeNode
|
||||
}: {
|
||||
moduleId: string;
|
||||
flowOutputList: FlowOutputItemType[];
|
||||
onChangeNode: FlowModuleItemType['onChangeNode'];
|
||||
}) => {
|
||||
const delOutputByKey = useCallback(
|
||||
(key: string) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'outputs',
|
||||
key: '',
|
||||
value: flowOutputList.filter((output) => output.key !== key)
|
||||
});
|
||||
},
|
||||
[moduleId, flowOutputList, onChangeNode]
|
||||
);
|
||||
const updateOutput = useCallback(
|
||||
(key: string, val: FlowOutputItemType) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'outputs',
|
||||
key: '',
|
||||
value: flowOutputList.map((output) => (output.key === key ? val : output))
|
||||
});
|
||||
},
|
||||
[flowOutputList, moduleId, onChangeNode]
|
||||
);
|
||||
const addUpdateOutput = useCallback(
|
||||
(val: FlowOutputItemType) => {
|
||||
onChangeNode({
|
||||
moduleId,
|
||||
type: 'outputs',
|
||||
key: '',
|
||||
value: flowOutputList.concat(val)
|
||||
});
|
||||
},
|
||||
[flowOutputList, moduleId, onChangeNode]
|
||||
);
|
||||
|
||||
const RenderOutput = ({ flowOutputList }: { flowOutputList: FlowOutputItemType[] }) => {
|
||||
return (
|
||||
<>
|
||||
{flowOutputList.map(
|
||||
(item) =>
|
||||
item.type !== FlowOutputItemTypeEnum.hidden && (
|
||||
<Box key={item.key} _notLast={{ mb: 7 }} position={'relative'}>
|
||||
<Label description={item.description}>{item.label}</Label>
|
||||
<Label
|
||||
moduleId={moduleId}
|
||||
onChangeNode={onChangeNode}
|
||||
outputKey={item.key}
|
||||
delOutputByKey={delOutputByKey}
|
||||
addUpdateOutput={addUpdateOutput}
|
||||
updateOutput={updateOutput}
|
||||
{...item}
|
||||
/>
|
||||
<Box mt={FlowOutputItemTypeEnum.answer ? 0 : 2} className={'nodrag'}>
|
||||
{item.type === FlowOutputItemTypeEnum.source && (
|
||||
<SourceHandle handleKey={item.key} valueType={item.valueType} />
|
||||
|
@@ -16,7 +16,7 @@ const TargetHandle = ({ handleKey, valueType, onConnect, ...props }: Props) => {
|
||||
valueType
|
||||
? FlowValueTypeStyle[valueType]
|
||||
: (FlowValueTypeStyle[FlowValueTypeEnum.any] as any),
|
||||
[]
|
||||
[valueType]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@@ -71,6 +71,9 @@ const NodeUserGuide = dynamic(() => import('./components/Nodes/NodeUserGuide'),
|
||||
const NodeExtract = dynamic(() => import('./components/Nodes/NodeExtract'), {
|
||||
ssr: false
|
||||
});
|
||||
const NodeHttp = dynamic(() => import('./components/Nodes/NodeHttp'), {
|
||||
ssr: false
|
||||
});
|
||||
|
||||
import 'reactflow/dist/style.css';
|
||||
import styles from './index.module.scss';
|
||||
@@ -87,7 +90,8 @@ const nodeTypes = {
|
||||
[FlowModuleTypeEnum.tfSwitchNode]: NodeTFSwitch,
|
||||
[FlowModuleTypeEnum.answerNode]: NodeAnswer,
|
||||
[FlowModuleTypeEnum.classifyQuestion]: NodeCQNode,
|
||||
[FlowModuleTypeEnum.contentExtract]: NodeExtract
|
||||
[FlowModuleTypeEnum.contentExtract]: NodeExtract,
|
||||
[FlowModuleTypeEnum.httpRequest]: NodeHttp
|
||||
// [FlowModuleTypeEnum.empty]: EmptyModule
|
||||
};
|
||||
const edgeTypes = {
|
||||
@@ -124,11 +128,10 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
const flow2AppModules = useCallback(() => {
|
||||
const modules: AppModuleItemType[] = nodes.map((item) => ({
|
||||
moduleId: item.data.moduleId,
|
||||
position: item.position,
|
||||
flowType: item.data.flowType,
|
||||
position: item.position,
|
||||
inputs: item.data.inputs.map((item) => ({
|
||||
key: item.key,
|
||||
value: item.value,
|
||||
...item,
|
||||
connected: item.type !== FlowInputItemTypeEnum.target
|
||||
})),
|
||||
outputs: item.data.outputs.map((item) => ({
|
||||
@@ -163,7 +166,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
return modules;
|
||||
}, [edges, nodes]);
|
||||
const onChangeNode = useCallback(
|
||||
({ moduleId, key, type = 'inputs', value, valueKey = 'value' }: FlowModuleItemChangeProps) => {
|
||||
({ moduleId, key, type = 'inputs', value }: FlowModuleItemChangeProps) => {
|
||||
setNodes((nodes) =>
|
||||
nodes.map((node) => {
|
||||
if (node.id !== moduleId) return node;
|
||||
@@ -172,18 +175,43 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
inputs: node.data.inputs.map((item) => {
|
||||
if (item.key === key) {
|
||||
return {
|
||||
...item,
|
||||
[valueKey]: value
|
||||
};
|
||||
}
|
||||
return item;
|
||||
})
|
||||
inputs: node.data.inputs.map((item) => (item.key === key ? value : item))
|
||||
}
|
||||
};
|
||||
}
|
||||
if (type === 'addInput') {
|
||||
const input = node.data.inputs.find((input) => input.key === value.key);
|
||||
if (input) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: 'key 重复'
|
||||
});
|
||||
return {
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
inputs: node.data.inputs
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
inputs: node.data.inputs.concat(value)
|
||||
}
|
||||
};
|
||||
}
|
||||
if (type === 'delInput') {
|
||||
return {
|
||||
...node,
|
||||
data: {
|
||||
...node.data,
|
||||
inputs: node.data.inputs.filter((item) => item.key !== key)
|
||||
}
|
||||
};
|
||||
}
|
||||
console.log(value);
|
||||
|
||||
return {
|
||||
...node,
|
||||
|
@@ -59,7 +59,7 @@ const FileSelect = ({
|
||||
)}
|
||||
{isCsv && (
|
||||
<Box
|
||||
my={3}
|
||||
mt={1}
|
||||
cursor={'pointer'}
|
||||
textDecoration={'underline'}
|
||||
color={'myBlue.600'}
|
||||
|
Reference in New Issue
Block a user