Skip to content

Commit

Permalink
feat: copy module
Browse files Browse the repository at this point in the history
  • Loading branch information
c121914yu committed Aug 7, 2023
1 parent 90406fc commit 206eb81
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 53 deletions.
4 changes: 4 additions & 0 deletions client/public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions client/public/locales/zh/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
},
"common": {
"Add": "添加",
"Cancel": "取消",
"Collect": "收藏",
"Copy": "复制",
"Delete": "删除",
"Filed is repeat": "",
"Filed is repeated": "字段重复了",
"Input": "输入",
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,6 +16,7 @@ type Props = {
minW?: string | number;
moduleId: string;
onDelNode: FlowModuleItemType['onDelNode'];
onCopyNode: FlowModuleItemType['onCopyNode'];
};

const NodeCard = ({
Expand All @@ -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 (
<Box
minW={minW}
Expand All @@ -52,19 +82,27 @@ const NodeCard = ({
</MyTooltip>
)}
<Box flex={1} />
<Flex
className={'nodrag'}
w={'22px'}
h={'22px'}
alignItems={'center'}
justifyContent={'center'}
color={'myGray.600'}
_hover={{ color: 'red.600' }}
cursor={'pointer'}
onClick={() => onDelNode(moduleId)}
>
<MyIcon name="delete" w={'16px'} />
</Flex>
<Menu autoSelect={false} isLazy>
<MenuButton
className={'nodrag'}
_hover={{ bg: 'myWhite.600' }}
cursor={'pointer'}
borderRadius={'md'}
onClick={(e) => {
e.stopPropagation();
}}
>
<MyIcon name={'more'} w={'14px'} p={2} />
</MenuButton>
<MenuList color={'myGray.700'} minW={`120px !important`} zIndex={10}>
{menuList.map((item) => (
<MenuItem key={item.label} onClick={item.onClick} py={[2, 3]}>
<MyIcon name={item.icon as any} w={['14px', '16px']} />
<Box ml={[1, 2]}>{item.label}</Box>
</MenuItem>
))}
</MenuList>
</Menu>
</Flex>
{children}
</Box>
Expand Down
122 changes: 86 additions & 36 deletions client/src/pages/app/detail/components/AdEdit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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) => ({
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -342,7 +380,7 @@ const AppEdit = ({ app, fullScreen, onFullScreen }: Props) => {
)
);
},
[onDelConnect, setEdges, nodes]
[nodes]
);

const { mutate: onclickSave, isLoading } = useRequest({
Expand Down Expand Up @@ -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 (
<>
Expand Down
2 changes: 2 additions & 0 deletions client/src/types/flow.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 8 additions & 2 deletions client/src/utils/adapt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<FlowModuleItemType> => {
// init some static data
const template =
Expand Down Expand Up @@ -109,7 +113,9 @@ export const appModule2FlowNode = ({
}),
onChangeNode,
onDelNode,
onDelEdge
onDelEdge,
onCopyNode,
onCollectionNode
};

return {
Expand Down

0 comments on commit 206eb81

Please sign in to comment.