From 2c174aa91c401cb371f420edaaa3314df29ad45e Mon Sep 17 00:00:00 2001
From: archer <545436317@qq.com>
Date: Wed, 16 Aug 2023 18:21:55 +0800
Subject: [PATCH] feat: copy settings config
---
client/public/locales/en/common.json | 12 +-
client/public/locales/zh/common.json | 12 +-
client/src/pages/api/chat/chatTest.ts | 8 ++
.../pages/api/openapi/v1/chat/completions.ts | 8 ++
.../AdEdit/components/ImportSettings.tsx | 54 +++++++
.../app/detail/components/AdEdit/index.tsx | 134 ++++++++++--------
client/src/pages/app/detail/index.tsx | 6 +-
client/src/utils/tools.ts | 8 +-
8 files changed, 173 insertions(+), 69 deletions(-)
create mode 100644 client/src/pages/app/detail/components/AdEdit/components/ImportSettings.tsx
diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json
index ad36fed86..f9061f459 100644
--- a/client/public/locales/en/common.json
+++ b/client/public/locales/en/common.json
@@ -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?",
diff --git a/client/public/locales/zh/common.json b/client/public/locales/zh/common.json
index 358db1193..ff42aba65 100644
--- a/client/public/locales/zh/common.json
+++ b/client/public/locales/zh/common.json
@@ -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": "确认删除该数据?",
diff --git a/client/src/pages/api/chat/chatTest.ts b/client/src/pages/api/chat/chatTest.ts
index ce3728ee1..6ba2560d4 100644
--- a/client/src/pages/api/chat/chatTest.ts
+++ b/client/src/pages/api/chat/chatTest.ts
@@ -81,3 +81,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
res.end();
}
}
+
+export const config = {
+ api: {
+ bodyParser: {
+ sizeLimit: '20mb'
+ }
+ }
+};
diff --git a/client/src/pages/api/openapi/v1/chat/completions.ts b/client/src/pages/api/openapi/v1/chat/completions.ts
index b9e30429c..01141b93a 100644
--- a/client/src/pages/api/openapi/v1/chat/completions.ts
+++ b/client/src/pages/api/openapi/v1/chat/completions.ts
@@ -446,3 +446,11 @@ export function responseStatus({
})
});
}
+
+export const config = {
+ api: {
+ bodyParser: {
+ sizeLimit: '20mb'
+ }
+ }
+};
diff --git a/client/src/pages/app/detail/components/AdEdit/components/ImportSettings.tsx b/client/src/pages/app/detail/components/AdEdit/components/ImportSettings.tsx
new file mode 100644
index 000000000..61f57955a
--- /dev/null
+++ b/client/src/pages/app/detail/components/AdEdit/components/ImportSettings.tsx
@@ -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 (
+
+
+
+
+
+
+
+ );
+};
+
+export default ImportSettings;
diff --git a/client/src/pages/app/detail/components/AdEdit/index.tsx b/client/src/pages/app/detail/components/AdEdit/index.tsx
index 81b2c9353..53a78e523 100644
--- a/client/src/pages/app/detail/components/AdEdit/index.tsx
+++ b/client/src/pages/app/detail/components/AdEdit/index.tsx
@@ -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(null);
const ChatTestRef = useRef(null);
+
const { updateAppDetail } = useUserStore();
const { x, y, zoom } = useViewport();
const [nodes, setNodes, onNodesChange] = useNodesState([]);
@@ -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();
@@ -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 ? (
- <>
-
- }
- borderRadius={'md'}
- borderColor={'myGray.300'}
- variant={'base'}
- aria-label={''}
- onClick={() => {
- onFullScreen(false);
- onFixView();
- }}
- />
-
-
- {app.name}
-
- >
- ) : (
- <>
-
- 应用编排
-
-
- }
- borderRadius={'lg'}
- variant={'base'}
- aria-label={'fullScreenLight'}
- onClick={() => {
- onFullScreen(true);
- onFixView();
- }}
- />
-
- >
- )}
+
+ }
+ borderRadius={'md'}
+ borderColor={'myGray.300'}
+ variant={'base'}
+ aria-label={''}
+ onClick={() => {
+ onCloseSettings();
+ onFixView();
+ }}
+ />
+
+
+ {app.name}
+
+
+
+ }
+ borderRadius={'lg'}
+ variant={'base'}
+ aria-label={'save'}
+ onClick={onOpenImport}
+ />
+
+
+ }
+ borderRadius={'lg'}
+ variant={'base'}
+ aria-label={'save'}
+ onClick={() =>
+ copyData(
+ JSON.stringify(flow2AppModules(), null, 2),
+ t('app.Export Config Successful')
+ )
+ }
+ />
+
+
{testModules ? (
}
variant={'base'}
color={'myGray.600'}
@@ -500,7 +512,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
) : (
}
borderRadius={'lg'}
aria-label={'save'}
@@ -591,20 +603,24 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
onClose={() => setTestModules(undefined)}
/>
+ {isOpenImport && (
+ {
+ setEdges([]);
+ setNodes([]);
+ setTimeout(() => {
+ initData(data);
+ }, 10);
+ }}
+ />
+ )}
>
);
};
const Flow = (data: Props) => (
-
+
{!!data.app._id && }
diff --git a/client/src/pages/app/detail/index.tsx b/client/src/pages/app/detail/index.tsx
index 27efbe464..ad4520e7b 100644
--- a/client/src/pages/app/detail/index.tsx
+++ b/client/src/pages/app/detail/index.tsx
@@ -171,11 +171,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
{currentTab === TabEnum.basicEdit && }
{currentTab === TabEnum.adEdit && appDetail && (
- setCurrentTab(TabEnum.basicEdit)}
- />
+ setCurrentTab(TabEnum.basicEdit)} />
)}
{currentTab === TabEnum.API && }
{currentTab === TabEnum.outLink && }
diff --git a/client/src/utils/tools.ts b/client/src/utils/tools.ts
index 92a88119c..618260957 100644
--- a/client/src/utils/tools.ts
+++ b/client/src/utils/tools.ts
@@ -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);