V4.6.5-alpha (#609)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure } from '@chakra-ui/react';
|
||||
import { PluginItemSchema } from '@fastgpt/global/core/plugin/type';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
@@ -7,10 +7,12 @@ import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { flowNode2Modules, useFlowProviderStore } from '@/components/core/module/Flow/FlowProvider';
|
||||
import { useFlowProviderStore } from '@/components/core/module/Flow/FlowProvider';
|
||||
import { flowNode2Modules } from '@/components/core/module/utils';
|
||||
import { putUpdatePlugin } from '@/web/core/plugin/api';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import { ModuleItemType } from '@fastgpt/global/core/module/type';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
|
||||
const ImportSettings = dynamic(() => import('@/components/core/module/Flow/ImportSettings'));
|
||||
const PreviewPlugin = dynamic(() => import('./Preview'));
|
||||
@@ -20,58 +22,83 @@ type Props = { plugin: PluginItemSchema; onClose: () => void };
|
||||
const Header = ({ plugin, onClose }: Props) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { copyData } = useCopyData();
|
||||
const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure();
|
||||
const { nodes, edges, onFixView } = useFlowProviderStore();
|
||||
const [previewModules, setPreviewModules] = React.useState<ModuleItemType[]>();
|
||||
|
||||
const { mutate: onclickSave, isLoading } = useRequest({
|
||||
mutationFn: () => {
|
||||
const modules = flowNode2Modules({ nodes, edges });
|
||||
const flow2ModulesAndCheck = useCallback(() => {
|
||||
const modules = flowNode2Modules({ nodes, edges });
|
||||
|
||||
// check required connect
|
||||
for (let i = 0; i < modules.length; i++) {
|
||||
const item = modules[i];
|
||||
// check required connect
|
||||
for (let i = 0; i < modules.length; i++) {
|
||||
const item = modules[i];
|
||||
|
||||
// update custom input connected
|
||||
if (item.flowType === FlowNodeTypeEnum.pluginInput) {
|
||||
item.inputs.forEach((item) => {
|
||||
item.connected = true;
|
||||
// update custom input connected
|
||||
if (item.flowType === FlowNodeTypeEnum.pluginInput) {
|
||||
item.inputs.forEach((item) => {
|
||||
item.connected = true;
|
||||
});
|
||||
if (item.outputs.find((output) => output.targets.length === 0)) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('module.Plugin input must connect')
|
||||
});
|
||||
if (item.outputs.find((output) => output.targets.length === 0)) {
|
||||
return Promise.reject(t('module.Plugin input must connect'));
|
||||
}
|
||||
}
|
||||
if (
|
||||
item.flowType === FlowNodeTypeEnum.pluginOutput &&
|
||||
item.inputs.find((input) => !input.connected)
|
||||
) {
|
||||
return Promise.reject(t('core.module.Plugin output must connect'));
|
||||
}
|
||||
|
||||
if (
|
||||
item.inputs.find((input) => {
|
||||
if (!input.required || input.connected) return false;
|
||||
if (!input.value || input.value === '' || input.value?.length === 0) return true;
|
||||
return false;
|
||||
})
|
||||
) {
|
||||
return Promise.reject(`【${item.name}】存在未填或未连接参数`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// plugin must have input
|
||||
const pluginInputModule = modules.find(
|
||||
(item) => item.flowType === FlowNodeTypeEnum.pluginInput
|
||||
);
|
||||
|
||||
if (!pluginInputModule) {
|
||||
return Promise.reject(t('module.Plugin input is required'));
|
||||
}
|
||||
if (pluginInputModule.inputs.length < 1) {
|
||||
return Promise.reject(t('module.Plugin input is not value'));
|
||||
if (
|
||||
item.flowType === FlowNodeTypeEnum.pluginOutput &&
|
||||
item.inputs.find((input) => !input.connected)
|
||||
) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('core.module.Plugin output must connect')
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
item.inputs.find((input) => {
|
||||
if (!input.required || input.connected) return false;
|
||||
if (!input.value || input.value === '' || input.value?.length === 0) return true;
|
||||
return false;
|
||||
})
|
||||
) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: `【${item.name}】存在未填或未连接参数`
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// plugin must have input
|
||||
const pluginInputModule = modules.find(
|
||||
(item) => item.flowType === FlowNodeTypeEnum.pluginInput
|
||||
);
|
||||
|
||||
if (!pluginInputModule) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('module.Plugin input is required')
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (pluginInputModule.inputs.length < 1) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('module.Plugin input is not value')
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return modules;
|
||||
}, [edges, nodes, t, toast]);
|
||||
|
||||
const { mutate: onclickSave, isLoading } = useRequest({
|
||||
mutationFn: (modules: ModuleItemType[]) => {
|
||||
return putUpdatePlugin({
|
||||
id: plugin._id,
|
||||
modules
|
||||
@@ -90,7 +117,7 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
alignItems={'center'}
|
||||
userSelect={'none'}
|
||||
>
|
||||
<MyTooltip label={'返回'} offset={[10, 10]}>
|
||||
<MyTooltip label={t('common.Back')} offset={[10, 10]}>
|
||||
<IconButton
|
||||
size={'sm'}
|
||||
icon={<MyIcon name={'back'} w={'14px'} />}
|
||||
@@ -125,12 +152,12 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
borderRadius={'lg'}
|
||||
variant={'base'}
|
||||
aria-label={'save'}
|
||||
onClick={() =>
|
||||
copyData(
|
||||
JSON.stringify(flowNode2Modules({ nodes, edges }), null, 2),
|
||||
t('app.Export Config Successful')
|
||||
)
|
||||
}
|
||||
onClick={() => {
|
||||
const modules = flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
copyData(JSON.stringify(modules, null, 2), t('app.Export Config Successful'));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<MyTooltip label={t('module.Preview Plugin')}>
|
||||
@@ -141,7 +168,10 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
aria-label={'save'}
|
||||
variant={'base'}
|
||||
onClick={() => {
|
||||
setPreviewModules(flowNode2Modules({ nodes, edges }));
|
||||
const modules = flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
setPreviewModules(modules);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
@@ -151,7 +181,12 @@ const Header = ({ plugin, onClose }: Props) => {
|
||||
borderRadius={'lg'}
|
||||
isLoading={isLoading}
|
||||
aria-label={'save'}
|
||||
onClick={onclickSave}
|
||||
onClick={() => {
|
||||
const modules = flow2ModulesAndCheck();
|
||||
if (modules) {
|
||||
onclickSave(modules);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
|
||||
@@ -3,7 +3,7 @@ import ReactFlow, { Background, ReactFlowProvider, useNodesState } from 'reactfl
|
||||
import { FlowModuleItemType, ModuleItemType } from '@fastgpt/global/core/module/type';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { formatPluginToPreviewModule } from '@fastgpt/global/core/module/utils';
|
||||
import { plugin2ModuleIO } from '@fastgpt/global/core/module/utils';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
@@ -37,7 +37,7 @@ const PreviewPlugin = ({
|
||||
avatar: plugin.avatar,
|
||||
name: plugin.name,
|
||||
intro: plugin.intro,
|
||||
...formatPluginToPreviewModule(plugin._id, modules)
|
||||
...plugin2ModuleIO(plugin._id, modules)
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
||||
@@ -24,10 +24,12 @@ const Render = ({ pluginId }: Props) => {
|
||||
const { nodes = [] } = useFlowProviderStore();
|
||||
const { pluginModuleTemplates, loadPluginTemplates } = usePluginStore();
|
||||
|
||||
const filterTemplates = useMemo(() => {
|
||||
const copyTemplates: FlowModuleTemplateType[] = JSON.parse(
|
||||
JSON.stringify(pluginSystemModuleTemplates)
|
||||
);
|
||||
const moduleTemplates = useMemo(() => {
|
||||
const pluginTemplates = pluginModuleTemplates.filter((item) => item.id !== pluginId);
|
||||
const concatTemplates = [...pluginSystemModuleTemplates, ...pluginTemplates];
|
||||
|
||||
const copyTemplates: FlowModuleTemplateType[] = JSON.parse(JSON.stringify(concatTemplates));
|
||||
|
||||
const filterType: Record<string, 1> = {
|
||||
[FlowNodeTypeEnum.userGuide]: 1,
|
||||
[FlowNodeTypeEnum.pluginInput]: 1,
|
||||
@@ -45,8 +47,13 @@ const Render = ({ pluginId }: Props) => {
|
||||
}
|
||||
});
|
||||
|
||||
// filter hideInPlugin inputs
|
||||
copyTemplates.forEach((template) => {
|
||||
template.inputs = template.inputs.filter((input) => !input.hideInPlugin);
|
||||
});
|
||||
|
||||
return copyTemplates;
|
||||
}, [nodes]);
|
||||
}, [nodes, pluginId, pluginModuleTemplates]);
|
||||
|
||||
const { data: pluginDetail } = useQuery(
|
||||
['getOnePlugin', pluginId],
|
||||
@@ -61,17 +68,12 @@ const Render = ({ pluginId }: Props) => {
|
||||
}
|
||||
}
|
||||
);
|
||||
console.log(pluginDetail);
|
||||
|
||||
useQuery(['getPlugTemplates'], () => loadPluginTemplates());
|
||||
const filterPlugins = useMemo(() => {
|
||||
return pluginModuleTemplates.filter((item) => item.id !== pluginId);
|
||||
}, [pluginId, pluginModuleTemplates]);
|
||||
|
||||
return pluginDetail ? (
|
||||
<Flow
|
||||
systemTemplates={filterTemplates}
|
||||
pluginTemplates={filterPlugins}
|
||||
templates={moduleTemplates}
|
||||
modules={pluginDetail?.modules || []}
|
||||
Header={<Header plugin={pluginDetail} onClose={() => router.back()} />}
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
Button,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
Input,
|
||||
Textarea,
|
||||
IconButton
|
||||
} from '@chakra-ui/react';
|
||||
import { Box, Flex, Button, ModalBody, Input, Textarea, IconButton } from '@chakra-ui/react';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
@@ -25,6 +16,8 @@ import { useTranslation } from 'next-i18next';
|
||||
import { useConfirm } from '@/web/common/hooks/useConfirm';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { CreateOnePluginParams } from '@fastgpt/global/core/plugin/controller';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||
|
||||
export type FormType = CreateOnePluginParams & {
|
||||
id?: string;
|
||||
@@ -35,7 +28,7 @@ export const defaultForm: FormType = {
|
||||
intro: '',
|
||||
modules: [
|
||||
{
|
||||
moduleId: 'w90mfp',
|
||||
moduleId: nanoid(),
|
||||
name: '定义插件输入',
|
||||
avatar: '/imgs/module/input.png',
|
||||
flowType: 'pluginInput',
|
||||
@@ -44,30 +37,11 @@ export const defaultForm: FormType = {
|
||||
x: 616.4226348688949,
|
||||
y: -165.05298493910115
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'question',
|
||||
valueType: 'string',
|
||||
type: 'target',
|
||||
label: '用户问题',
|
||||
required: true,
|
||||
edit: true,
|
||||
connected: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'question',
|
||||
valueType: 'string',
|
||||
label: '用户问题',
|
||||
type: 'source',
|
||||
edit: true,
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
inputs: [],
|
||||
outputs: []
|
||||
},
|
||||
{
|
||||
moduleId: 'tze1ju',
|
||||
moduleId: nanoid(),
|
||||
name: '定义插件输出',
|
||||
avatar: '/imgs/module/output.png',
|
||||
flowType: 'pluginOutput',
|
||||
@@ -76,27 +50,8 @@ export const defaultForm: FormType = {
|
||||
x: 1607.7142331269126,
|
||||
y: -151.8669210746189
|
||||
},
|
||||
inputs: [
|
||||
{
|
||||
key: 'answer',
|
||||
type: 'target',
|
||||
valueType: 'string',
|
||||
label: '答案',
|
||||
required: true,
|
||||
edit: true,
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'answer',
|
||||
valueType: 'string',
|
||||
label: '答案',
|
||||
type: 'source',
|
||||
edit: true,
|
||||
targets: []
|
||||
}
|
||||
]
|
||||
inputs: [],
|
||||
outputs: []
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -127,7 +82,7 @@ const CreateModal = ({
|
||||
});
|
||||
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
fileType: '.jpg,.png,.svg',
|
||||
fileType: 'image/*',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user