mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 12:20:34 +00:00
feat: copy settings config
This commit is contained in:
@@ -11,9 +11,15 @@
|
||||
"Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!",
|
||||
"Connection is invalid": "Connecting is invalid",
|
||||
"Connection type is different": "Connection type is different",
|
||||
"Export Config Successful": "The configuration has been copied. Please check for important data",
|
||||
"Export Configs": "Export Configs",
|
||||
"Import Config": "Import Config",
|
||||
"Import Config Failed": "Failed to import the configuration, please ensure that the configuration is normal!",
|
||||
"Import Configs": "Import Configs",
|
||||
"Input Field Settings": "Input Field Settings",
|
||||
"My Apps": "My Apps",
|
||||
"Output Field Settings": "Output Field Settings"
|
||||
"Output Field Settings": "Output Field Settings",
|
||||
"Paste Config": "Paste Config"
|
||||
},
|
||||
"chat": {
|
||||
"Complete Response": "Complete Response",
|
||||
@@ -31,12 +37,14 @@
|
||||
"Cancel": "Cancel",
|
||||
"Collect": "Collect",
|
||||
"Copy": "Copy",
|
||||
"Copy Successful": "Copy Successful",
|
||||
"Course": "",
|
||||
"Delete": "Delete",
|
||||
"Filed is repeat": "Filed is repeated",
|
||||
"Filed is repeated": "",
|
||||
"Input": "Input",
|
||||
"Output": "Output"
|
||||
"Output": "Output",
|
||||
"export": ""
|
||||
},
|
||||
"dataset": {
|
||||
"Confirm to delete the data": "Confirm to delete the data?",
|
||||
|
@@ -11,9 +11,15 @@
|
||||
"Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!",
|
||||
"Connection is invalid": "连接无效",
|
||||
"Connection type is different": "连接的类型不一致",
|
||||
"Export Config Successful": "已复制配置,请注意检查是否有重要数据",
|
||||
"Export Configs": "导出配置",
|
||||
"Import Config": "导入配置",
|
||||
"Import Config Failed": "导入配置失败,请确保配置正常!",
|
||||
"Import Configs": "导入配置",
|
||||
"Input Field Settings": "输入字段编辑",
|
||||
"My Apps": "我的应用",
|
||||
"Output Field Settings": "输出字段编辑"
|
||||
"Output Field Settings": "输出字段编辑",
|
||||
"Paste Config": "粘贴配置"
|
||||
},
|
||||
"chat": {
|
||||
"Complete Response": "完整响应",
|
||||
@@ -31,12 +37,14 @@
|
||||
"Cancel": "取消",
|
||||
"Collect": "收藏",
|
||||
"Copy": "复制",
|
||||
"Copy Successful": "复制成功",
|
||||
"Course": "",
|
||||
"Delete": "删除",
|
||||
"Filed is repeat": "",
|
||||
"Filed is repeated": "字段重复了",
|
||||
"Input": "输入",
|
||||
"Output": "输出"
|
||||
"Output": "输出",
|
||||
"export": ""
|
||||
},
|
||||
"dataset": {
|
||||
"Confirm to delete the data": "确认删除该数据?",
|
||||
|
@@ -81,3 +81,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: {
|
||||
sizeLimit: '20mb'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -446,3 +446,11 @@ export function responseStatus({
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: {
|
||||
sizeLimit: '20mb'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -0,0 +1,54 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Textarea, Button, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import { AppModuleItemType } from '@/types/app';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
|
||||
const ImportSettings = ({
|
||||
onClose,
|
||||
onSuccess
|
||||
}: {
|
||||
onClose: () => void;
|
||||
onSuccess: (modules: AppModuleItemType[]) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
return (
|
||||
<MyModal isOpen w={'600px'} onClose={onClose} title={t('app.Import Config')}>
|
||||
<ModalBody>
|
||||
<Textarea
|
||||
placeholder={t('app.Paste Config') || 'app.Paste Config'}
|
||||
defaultValue={value}
|
||||
rows={16}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="base"
|
||||
onClick={() => {
|
||||
if (!value) {
|
||||
return onClose();
|
||||
}
|
||||
try {
|
||||
const data = JSON.parse(value);
|
||||
onSuccess(data);
|
||||
onClose();
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: t('app.Import Config Failed')
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImportSettings;
|
@@ -10,7 +10,7 @@ import ReactFlow, {
|
||||
Connection,
|
||||
useViewport
|
||||
} from 'reactflow';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure, position } from '@chakra-ui/react';
|
||||
import { Box, Flex, IconButton, useTheme, useDisclosure } from '@chakra-ui/react';
|
||||
import { SmallCloseIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
edgeOptions,
|
||||
@@ -33,6 +33,7 @@ import type { AppSchema } from '@/types/mongoSchema';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useCopyData } from '@/utils/tools';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import MyIcon from '@/components/Icon';
|
||||
@@ -41,6 +42,9 @@ import MyTooltip from '@/components/MyTooltip';
|
||||
import TemplateList from './components/TemplateList';
|
||||
import ChatTest, { type ChatTestComponentRef } from './components/ChatTest';
|
||||
|
||||
const ImportSettings = dynamic(() => import('./components/ImportSettings'), {
|
||||
ssr: false
|
||||
});
|
||||
const NodeChat = dynamic(() => import('./components/Nodes/NodeChat'), {
|
||||
ssr: false
|
||||
});
|
||||
@@ -98,14 +102,17 @@ const nodeTypes = {
|
||||
const edgeTypes = {
|
||||
buttonedge: ButtonEdge
|
||||
};
|
||||
type Props = { app: AppSchema; fullScreen: boolean; onFullScreen: (val: boolean) => void };
|
||||
type Props = { app: AppSchema; onCloseSettings: () => void };
|
||||
|
||||
const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
const AppEdit = ({ app, onCloseSettings }: Props) => {
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
const { t } = useTranslation();
|
||||
const { copyData } = useCopyData();
|
||||
|
||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||
const ChatTestRef = useRef<ChatTestComponentRef>(null);
|
||||
|
||||
const { updateAppDetail } = useUserStore();
|
||||
const { x, y, zoom } = useViewport();
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<FlowModuleItemType>([]);
|
||||
@@ -115,6 +122,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
onOpen: onOpenTemplate,
|
||||
onClose: onCloseTemplate
|
||||
} = useDisclosure();
|
||||
const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure();
|
||||
|
||||
const [testModules, setTestModules] = useState<AppModuleItemType[]>();
|
||||
|
||||
@@ -398,15 +406,15 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
});
|
||||
|
||||
const initData = useCallback(
|
||||
(app: AppSchema) => {
|
||||
(modules: AppModuleItemType[]) => {
|
||||
const edges = appModule2FlowEdge({
|
||||
modules: app.modules,
|
||||
modules,
|
||||
onDelete: onDelConnect
|
||||
});
|
||||
setEdges(edges);
|
||||
|
||||
setNodes(
|
||||
app.modules.map((item) =>
|
||||
modules.map((item) =>
|
||||
appModule2FlowNode({
|
||||
item,
|
||||
onChangeNode,
|
||||
@@ -434,8 +442,8 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
initData(JSON.parse(JSON.stringify(app)));
|
||||
}, [app]);
|
||||
initData(JSON.parse(JSON.stringify(app.modules)));
|
||||
}, [app.modules]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -447,49 +455,53 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
alignItems={'center'}
|
||||
userSelect={'none'}
|
||||
>
|
||||
{fullScreen ? (
|
||||
<>
|
||||
<MyTooltip label={'返回'} offset={[10, 10]}>
|
||||
<IconButton
|
||||
size={'sm'}
|
||||
icon={<MyIcon name={'back'} w={'14px'} />}
|
||||
borderRadius={'md'}
|
||||
borderColor={'myGray.300'}
|
||||
variant={'base'}
|
||||
aria-label={''}
|
||||
onClick={() => {
|
||||
onFullScreen(false);
|
||||
onFixView();
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Box ml={5} fontSize={['lg', '2xl']} flex={1}>
|
||||
{app.name}
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Box fontSize={['lg', '2xl']} flex={1}>
|
||||
应用编排
|
||||
</Box>
|
||||
<MyTooltip label={'全屏'}>
|
||||
<IconButton
|
||||
mr={6}
|
||||
icon={<MyIcon name={'fullScreenLight'} w={['14px', '16px']} />}
|
||||
borderRadius={'lg'}
|
||||
variant={'base'}
|
||||
aria-label={'fullScreenLight'}
|
||||
onClick={() => {
|
||||
onFullScreen(true);
|
||||
onFixView();
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
</>
|
||||
)}
|
||||
<MyTooltip label={'返回'} offset={[10, 10]}>
|
||||
<IconButton
|
||||
size={'sm'}
|
||||
icon={<MyIcon name={'back'} w={'14px'} />}
|
||||
borderRadius={'md'}
|
||||
borderColor={'myGray.300'}
|
||||
variant={'base'}
|
||||
aria-label={''}
|
||||
onClick={() => {
|
||||
onCloseSettings();
|
||||
onFixView();
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<Box ml={[3, 6]} fontSize={['md', '2xl']} flex={1}>
|
||||
{app.name}
|
||||
</Box>
|
||||
|
||||
<MyTooltip label={t('app.Import Configs')}>
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
icon={<MyIcon name={'importLight'} w={['14px', '16px']} />}
|
||||
borderRadius={'lg'}
|
||||
variant={'base'}
|
||||
aria-label={'save'}
|
||||
onClick={onOpenImport}
|
||||
/>
|
||||
</MyTooltip>
|
||||
<MyTooltip label={t('app.Export Configs')}>
|
||||
<IconButton
|
||||
mr={[3, 6]}
|
||||
icon={<MyIcon name={'export'} w={['14px', '16px']} />}
|
||||
borderRadius={'lg'}
|
||||
variant={'base'}
|
||||
aria-label={'save'}
|
||||
onClick={() =>
|
||||
copyData(
|
||||
JSON.stringify(flow2AppModules(), null, 2),
|
||||
t('app.Export Config Successful')
|
||||
)
|
||||
}
|
||||
/>
|
||||
</MyTooltip>
|
||||
|
||||
{testModules ? (
|
||||
<IconButton
|
||||
mr={6}
|
||||
mr={[3, 6]}
|
||||
icon={<SmallCloseIcon fontSize={'25px'} />}
|
||||
variant={'base'}
|
||||
color={'myGray.600'}
|
||||
@@ -500,7 +512,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
) : (
|
||||
<MyTooltip label={'测试对话'}>
|
||||
<IconButton
|
||||
mr={6}
|
||||
mr={[3, 6]}
|
||||
icon={<MyIcon name={'chat'} w={['14px', '16px']} />}
|
||||
borderRadius={'lg'}
|
||||
aria-label={'save'}
|
||||
@@ -591,20 +603,24 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
|
||||
onClose={() => setTestModules(undefined)}
|
||||
/>
|
||||
</Box>
|
||||
{isOpenImport && (
|
||||
<ImportSettings
|
||||
onClose={onCloseImport}
|
||||
onSuccess={(data) => {
|
||||
setEdges([]);
|
||||
setNodes([]);
|
||||
setTimeout(() => {
|
||||
initData(data);
|
||||
}, 10);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Flow = (data: Props) => (
|
||||
<Box
|
||||
h={'100%'}
|
||||
position={data.fullScreen ? 'fixed' : 'relative'}
|
||||
zIndex={999}
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
>
|
||||
<Box h={'100%'} position={'fixed'} zIndex={999} top={0} left={0} right={0} bottom={0}>
|
||||
<ReactFlowProvider>
|
||||
<Flex h={'100%'} flexDirection={'column'} bg={'#fff'}>
|
||||
{!!data.app._id && <AppEdit {...data} />}
|
||||
|
@@ -171,11 +171,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
||||
<Box flex={'1 0 0'} h={[0, '100%']} overflow={['overlay', '']}>
|
||||
{currentTab === TabEnum.basicEdit && <BasicEdit appId={appId} />}
|
||||
{currentTab === TabEnum.adEdit && appDetail && (
|
||||
<AdEdit
|
||||
app={appDetail}
|
||||
fullScreen={true}
|
||||
onFullScreen={() => setCurrentTab(TabEnum.basicEdit)}
|
||||
/>
|
||||
<AdEdit app={appDetail} onCloseSettings={() => setCurrentTab(TabEnum.basicEdit)} />
|
||||
)}
|
||||
{currentTab === TabEnum.API && <API appId={appId} />}
|
||||
{currentTab === TabEnum.outLink && <OutLink appId={appId} />}
|
||||
|
@@ -1,15 +1,21 @@
|
||||
import crypto from 'crypto';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import dayjs from 'dayjs';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
/**
|
||||
* copy text data
|
||||
*/
|
||||
export const useCopyData = () => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
return {
|
||||
copyData: async (data: string, title: string = '复制成功', duration = 1000) => {
|
||||
copyData: async (
|
||||
data: string,
|
||||
title: string | null = t('common.Copy Successful'),
|
||||
duration = 1000
|
||||
) => {
|
||||
try {
|
||||
if (navigator.clipboard) {
|
||||
await navigator.clipboard.writeText(data);
|
||||
|
Reference in New Issue
Block a user