diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json
index 804b25c5b..5803c3fcd 100644
--- a/client/public/locales/en/common.json
+++ b/client/public/locales/en/common.json
@@ -24,6 +24,10 @@
},
"common": {
"Add": "Add",
+ "Cancel": "Cancel",
+ "Collect": "Collect",
+ "Copy": "Copy",
+ "Delete": "Delete",
"Filed is repeat": "Filed is repeated",
"Filed is repeated": "",
"Input": "Input",
diff --git a/client/public/locales/zh/common.json b/client/public/locales/zh/common.json
index 12d4551f5..0a0069e7d 100644
--- a/client/public/locales/zh/common.json
+++ b/client/public/locales/zh/common.json
@@ -24,6 +24,10 @@
},
"common": {
"Add": "添加",
+ "Cancel": "取消",
+ "Collect": "收藏",
+ "Copy": "复制",
+ "Delete": "删除",
"Filed is repeat": "",
"Filed is repeated": "字段重复了",
"Input": "输入",
diff --git a/client/src/pages/app/detail/components/AdEdit/components/modules/NodeCard.tsx b/client/src/pages/app/detail/components/AdEdit/components/modules/NodeCard.tsx
index 89fc3e099..0dd4fcdaf 100644
--- a/client/src/pages/app/detail/components/AdEdit/components/modules/NodeCard.tsx
+++ b/client/src/pages/app/detail/components/AdEdit/components/modules/NodeCard.tsx
@@ -1,10 +1,11 @@
-import React from 'react';
-import { Box, Flex, useTheme } from '@chakra-ui/react';
+import React, { useMemo } from 'react';
+import { Box, Flex, useTheme, Menu, MenuButton, MenuList, MenuItem } from '@chakra-ui/react';
import MyIcon from '@/components/Icon';
import Avatar from '@/components/Avatar';
import type { FlowModuleItemType } from '@/types/flow';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
+import { useTranslation } from 'react-i18next';
type Props = {
children?: React.ReactNode | React.ReactNode[] | string;
@@ -15,6 +16,7 @@ type Props = {
minW?: string | number;
moduleId: string;
onDelNode: FlowModuleItemType['onDelNode'];
+ onCopyNode: FlowModuleItemType['onCopyNode'];
};
const NodeCard = ({
@@ -23,11 +25,39 @@ const NodeCard = ({
name = '未知模块',
description,
minW = '300px',
+ onCopyNode,
onDelNode,
moduleId
}: Props) => {
+ const { t } = useTranslation();
const theme = useTheme();
+ const menuList = useMemo(
+ () => [
+ {
+ icon: 'copy',
+ label: t('common.Copy'),
+ onClick: () => onCopyNode(moduleId)
+ },
+ {
+ icon: 'delete',
+ label: t('common.Delete'),
+ onClick: () => onDelNode(moduleId)
+ },
+ // {
+ // icon: 'collectionLight',
+ // label: t('common.Collect'),
+ // onClick: () => {}
+ // },
+ {
+ icon: 'back',
+ label: t('common.Cancel'),
+ onClick: () => {}
+ }
+ ],
+ [moduleId, onCopyNode, onDelNode, t]
+ );
+
return (
)}
- onDelNode(moduleId)}
- >
-
-
+
{children}
diff --git a/client/src/pages/app/detail/components/AdEdit/index.tsx b/client/src/pages/app/detail/components/AdEdit/index.tsx
index ac60798f5..81b2c9353 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 } from '@chakra-ui/react';
+import { Box, Flex, IconButton, useTheme, useDisclosure, position } from '@chakra-ui/react';
import { SmallCloseIcon } from '@chakra-ui/icons';
import {
edgeOptions,
@@ -126,6 +126,32 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
}, 100);
}, []);
+ const onAddNode = useCallback(
+ ({ template, position }: { template: FlowModuleTemplateType; position: XYPosition }) => {
+ if (!reactFlowWrapper.current) return;
+ const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
+ const mouseX = (position.x - reactFlowBounds.left - x) / zoom - 100;
+ const mouseY = (position.y - reactFlowBounds.top - y) / zoom;
+
+ setNodes((state) =>
+ state.concat(
+ appModule2FlowNode({
+ item: {
+ ...template,
+ moduleId: nanoid(),
+ position: { x: mouseX, y: mouseY }
+ },
+ onChangeNode,
+ onDelNode,
+ onDelEdge,
+ onCopyNode,
+ onCollectionNode
+ })
+ )
+ );
+ },
+ [x, zoom, y]
+ );
const onDelNode = useCallback(
(nodeId: string) => {
setNodes((state) => state.filter((item) => item.id !== nodeId));
@@ -155,6 +181,45 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
},
[setEdges]
);
+ const onCopyNode = useCallback(
+ (nodeId: string) => {
+ setNodes((nodes) => {
+ const node = nodes.find((node) => node.id === nodeId);
+ if (!node) return nodes;
+ const template = {
+ logo: node.data.logo,
+ name: node.data.name,
+ intro: node.data.intro,
+ description: node.data.description,
+ flowType: node.data.flowType,
+ inputs: node.data.inputs,
+ outputs: node.data.outputs,
+ showStatus: node.data.showStatus
+ };
+ return nodes.concat(
+ appModule2FlowNode({
+ item: {
+ ...template,
+ moduleId: nanoid(),
+ position: { x: node.position.x + 200, y: node.position.y + 50 }
+ },
+ onChangeNode,
+ onDelNode,
+ onDelEdge,
+ onCopyNode,
+ onCollectionNode
+ })
+ );
+ });
+ },
+ [setNodes]
+ );
+ const onCollectionNode = useCallback(
+ (nodeId: string) => {
+ console.log(nodes.find((node) => node.id === nodeId));
+ },
+ [nodes]
+ );
const flow2AppModules = useCallback(() => {
const modules: AppModuleItemType[] = nodes.map((item) => ({
@@ -264,39 +329,12 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
})
);
},
- [setNodes]
+ []
);
- const onAddNode = useCallback(
- ({ template, position }: { template: FlowModuleTemplateType; position: XYPosition }) => {
- if (!reactFlowWrapper.current) return;
- const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
- const mouseX = (position.x - reactFlowBounds.left - x) / zoom - 100;
- const mouseY = (position.y - reactFlowBounds.top - y) / zoom;
-
- setNodes((state) =>
- state.concat(
- appModule2FlowNode({
- item: {
- ...template,
- moduleId: nanoid(),
- position: { x: mouseX, y: mouseY }
- },
- onChangeNode,
- onDelNode,
- onDelEdge
- })
- )
- );
- },
- [onDelEdge, onChangeNode, onDelNode, setNodes, x, y, zoom]
- );
- const onDelConnect = useCallback(
- (id: string) => {
- setEdges((state) => state.filter((item) => item.id !== id));
- },
- [setEdges]
- );
+ const onDelConnect = useCallback((id: string) => {
+ setEdges((state) => state.filter((item) => item.id !== id));
+ }, []);
const onConnect = useCallback(
({ connect }: { connect: Connection }) => {
const source = nodes.find((node) => node.id === connect.source)?.data;
@@ -342,7 +380,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
)
);
},
- [onDelConnect, setEdges, nodes]
+ [nodes]
);
const { mutate: onclickSave, isLoading } = useRequest({
@@ -373,19 +411,31 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
item,
onChangeNode,
onDelNode,
- onDelEdge
+ onDelEdge,
+ onCopyNode,
+ onCollectionNode
})
)
);
onFixView();
},
- [onDelConnect, setEdges, setNodes, onFixView, onChangeNode, onDelNode, onDelEdge]
+ [
+ onDelConnect,
+ setEdges,
+ setNodes,
+ onFixView,
+ onChangeNode,
+ onDelNode,
+ onDelEdge,
+ onCopyNode,
+ onCollectionNode
+ ]
);
useEffect(() => {
initData(JSON.parse(JSON.stringify(app)));
- }, [app, initData]);
+ }, [app]);
return (
<>
diff --git a/client/src/types/flow.d.ts b/client/src/types/flow.d.ts
index 3cb39f3fa..6c428e485 100644
--- a/client/src/types/flow.d.ts
+++ b/client/src/types/flow.d.ts
@@ -61,6 +61,8 @@ export type FlowModuleItemType = FlowModuleTemplateType & {
moduleId: string;
onChangeNode: (e: FlowModuleItemChangeProps) => void;
onDelNode: (id: string) => void;
+ onCopyNode: (id: string) => void;
+ onCollectionNode: (id: string) => void;
onDelEdge: ({
moduleId,
sourceHandle,
diff --git a/client/src/utils/adapt.ts b/client/src/utils/adapt.ts
index fa6ace9c3..8c1f178de 100644
--- a/client/src/utils/adapt.ts
+++ b/client/src/utils/adapt.ts
@@ -64,12 +64,16 @@ export const appModule2FlowNode = ({
item,
onChangeNode,
onDelNode,
- onDelEdge
+ onDelEdge,
+ onCopyNode,
+ onCollectionNode
}: {
item: AppModuleItemType;
onChangeNode: FlowModuleItemType['onChangeNode'];
onDelNode: FlowModuleItemType['onDelNode'];
onDelEdge: FlowModuleItemType['onDelEdge'];
+ onCopyNode: FlowModuleItemType['onCopyNode'];
+ onCollectionNode: FlowModuleItemType['onCollectionNode'];
}): Node => {
// init some static data
const template =
@@ -109,7 +113,9 @@ export const appModule2FlowNode = ({
}),
onChangeNode,
onDelNode,
- onDelEdge
+ onDelEdge,
+ onCopyNode,
+ onCollectionNode
};
return {