diff --git a/docs/caseShow/cicdPipeLine.md b/docs/caseShow/cicdPipeLine.md index 220f9bb..837ea3a 100644 --- a/docs/caseShow/cicdPipeLine.md +++ b/docs/caseShow/cicdPipeLine.md @@ -10,4 +10,4 @@ description: ## Default - + diff --git a/docs/caseShow/dataFlow.md b/docs/caseShow/dataFlow.md new file mode 100644 index 0000000..5d60769 --- /dev/null +++ b/docs/caseShow/dataFlow.md @@ -0,0 +1,25 @@ +--- +nav: + title: 案例展示 + order: 100 +group: + title: 场景展示 + order: 1 +title: 数据流程图 +order: 1 +description: +--- + +## 数据流程图 + +案例特点: + +- 点击选中节点,自动高亮相关链路 +- 无级缩放的 label +- 节点拖拽 +- Danger 节点链路高亮 +- 选中节点唤起抽屉 +- 点击画布或其他节点重置选中态 +- 初始化自动布局 + + diff --git a/docs/caseShow/demos/dataflow/data.tsx b/docs/caseShow/demos/dataflow/data.tsx new file mode 100644 index 0000000..472717e --- /dev/null +++ b/docs/caseShow/demos/dataflow/data.tsx @@ -0,0 +1,261 @@ +import { FlowViewEdge, FlowViewNode } from '@ant-design/pro-flow'; +import { Progress } from 'antd'; +import React from 'react'; +import useStyles from './styled'; + +const ApiScore: React.FC<{ score: number }> = ({ score }) => { + return ( + 60 ? '#30a46c' : '#e5484d'} + format={() => `${score}`} + size={28} + /> + ); +}; + +const DangerLogo: React.FC = () => { + const { styles } = useStyles(); + + return ( +
+ +
+ ); +}; + +export const nodes: FlowViewNode[] = [ + { + id: 'a1', + label: '数据源', + data: { + title: 'XXX_API_ddddddddddddddddddddddddddddddƒddddddddddddddddddddddddb1', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*jWDsQ5GTmHMAAAAAAAAAAAAADvuvAQ/original', + description: 'XXX_XXX_XXX_API', + }, + }, + { + id: 'b1', + label: ' API', + data: { + isDanger: true, + title: 'XXX_API_ddddddddddddddddddddddddddddddddddddddddddddddddddddddb1', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + description: 'XXX_XXX_XXX_API', + titleSlot: { + type: 'left', + value: , + }, + }, + }, + { + id: 'b2', + data: { + title: 'XXX_APIddddddddddddddddddddddddddddddddddddddddddddddddddd_b2', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + description: 'XXX_XXX_XXX_API', + titleSlot: { + type: 'right', + value: , + }, + }, + }, + { + id: 'b3', + data: { + title: 'XXX_APIddddddddddddddddddddddddddddddddddddddddddddddddddd_b2', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + description: 'XXX_XXX_XXX_API', + }, + }, + { + id: 'b4', + data: { + title: 'XXX_API_b4', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*kgyiRKi04eUAAAAAAAAAAAAADvuvAQ/original', + description: 'XXX_XXX_XXX_API', + }, + }, + { + id: 'c1', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', + description: '2031030213014', + }, + }, + { + id: 'c2', + label: '产品', + data: { + isDanger: true, + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', + description: '2031030213014', + }, + }, + { + id: 'c3', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*ezaYT4wYRBwAAAAAAAAAAAAADvuvAQ/original', + description: '2031030213014', + }, + }, + { + id: 'd1', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*8KacSbGkJxIAAAAAAAAAAAAADvuvAQ/original', + description: '2031030213014', + }, + }, + { + id: 'd2', + type: 'BasicNodeGroup', + label: '用户', + data: [ + { + id: 'a5', + data: { + title: 'XXX数据源', + description: 'cksadjfnf', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*8KacSbGkJxIAAAAAAAAAAAAADvuvAQ/original', + }, + }, + { + id: 'a6', + data: { + title: 'XXX_API', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*8KacSbGkJxIAAAAAAAAAAAAADvuvAQ/original', + description: 'XXX_XXX_XXX_API', + }, + }, + { + id: 'a7', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*8KacSbGkJxIAAAAAAAAAAAAADvuvAQ/original', + description: '2031030213014', + }, + }, + { + id: 'a8', + data: { + title: 'XXX数据源', + description: 'cksadjfnf', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*8KacSbGkJxIAAAAAAAAAAAAADvuvAQ/original', + }, + }, + { + id: 'a9', + data: { + title: 'XXX_API', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*8KacSbGkJxIAAAAAAAAAAAAADvuvAQ/original', + description: 'XXX_XXX_XXX_API', + }, + }, + { + id: 'a10', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*8KacSbGkJxIAAAAAAAAAAAAADvuvAQ/original', + description: '2031030213014', + }, + }, + { + id: 'a11', + data: { + title: 'XXXX产品', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*8KacSbGkJxIAAAAAAAAAAAAADvuvAQ/original', + description: '2031030213014', + }, + }, + ], + }, + { + id: 'd3', + data: { + title: 'XXXX用户', + logo: 'https://mdn.alipayobjects.com/huamei_ntgeqc/afts/img/A*8KacSbGkJxIAAAAAAAAAAAAADvuvAQ/original', + description: '2031030213014', + }, + }, +]; + +export const edges: FlowViewEdge[] = [ + { + id: 'a1-b1', + source: 'a1', + target: 'b1', + }, + { + id: 'a1-b2', + source: 'a1', + target: 'b2', + }, + { + id: 'a1-b3', + source: 'a1', + target: 'b3', + }, + { + id: 'a1-b4', + source: 'a1', + target: 'b4', + }, + + { + id: 'b2-c1', + source: 'b2', + target: 'c1', + type: 'radius', + }, + { + id: 'b3-c1', + source: 'b3', + target: 'c1', + type: 'radius', + }, + { + id: 'b1-c2', + source: 'b1', + target: 'c2', + type: 'radius', + }, + { + id: 'c2-d2', + source: 'c2', + target: 'd2', + type: 'radius', + }, + { + id: 'b4-c3', + source: 'b4', + target: 'c3', + type: 'radius', + }, + { + id: 'c1-d1', + source: 'c1', + target: 'd1', + type: 'radius', + }, + { + id: 'c3-d3', + source: 'c3', + target: 'd3', + type: 'radius', + }, +]; diff --git a/docs/caseShow/demos/dataflow/helper.ts b/docs/caseShow/demos/dataflow/helper.ts new file mode 100644 index 0000000..68e653d --- /dev/null +++ b/docs/caseShow/demos/dataflow/helper.ts @@ -0,0 +1,106 @@ +import { SelectType } from '@ant-design/pro-flow'; + +export const formatNodeMapping = (nodes, edges) => { + const obj = {}; + nodes.forEach((node) => { + obj[node.id] = { ...node }; + obj[node.id].left = getLeftNode(nodes, edges, node.id); + obj[node.id].right = getRightNode(nodes, edges, node.id); + }); + return obj; +}; + +export const formatEdgeMapping = (nodes, edges) => { + const obj = {}; + edges.forEach((edge) => { + obj[edge.id] = { ...edge }; + obj[edge.id].targetNode = nodes.find((node) => node.id === edge.target); + obj[edge.id].sourceNode = nodes.find((node) => node.id === edge.source); + }); + return obj; +}; + +export const setNodeDanger = (nodeMapping) => { + Object.keys(nodeMapping).forEach((key) => { + const node = nodeMapping[key]; + if (node.data.isDanger) { + const highNodes = getAllHighLightNode(nodeMapping, node.id); + + highNodes.forEach((item) => { + if ([SelectType.SELECT, SelectType.SUB_SELECT].includes(nodeMapping[item.id].select)) + return; + nodeMapping[item.id].select = SelectType.SUB_DANGER; + }); + } + }); + + return nodeMapping; +}; + +export const setEdgeDanger = (edgeMapping) => { + Object.keys(edgeMapping).forEach((key) => { + const edge = edgeMapping[key]; + + if (edge.targetNode.data.isDanger || edge.sourceNode.data.isDanger) { + if ([SelectType.SELECT, SelectType.SUB_SELECT].includes(edge.select)) return; + edge.select = SelectType.SUB_DANGER; + } + }); + + return edgeMapping; +}; + +export const getLeftNode = (nodes, edges, currentId) => { + return edges + .filter((edge) => { + return edge.target === currentId; + }) + .map((edge) => { + const node = nodes.find((node) => node.id === edge.source); + return node.id; + }); +}; + +export const getRightNode = (nodes, edges, currentId) => { + return edges + .filter((edge) => { + return edge.source === currentId; + }) + .map((edge) => { + const node = nodes.find((node) => node.id === edge.target); + return node.id; + }); +}; + +export const getAllHighLightNode = (nodeMapping, currentId) => { + const leftNodes = []; + const rightNodes = []; + + function getLeft(nodeId) { + if (nodeMapping[nodeId] === undefined) return; + + const lefts = nodeMapping[nodeId].left; + if (lefts.length === 0) return; + + leftNodes.push(...lefts.map((item: string) => nodeMapping[item])); + lefts.forEach((node) => { + getLeft(node); + }); + } + + function getRight(nodeId) { + if (nodeMapping[nodeId] === undefined) return; + + const rights = nodeMapping[nodeId].right; + if (rights.length === 0) return; + + rightNodes.push(...rights.map((item) => nodeMapping[item])); + rights.forEach((node) => { + getRight(node); + }); + } + getLeft(currentId); + getRight(currentId); + + return [...leftNodes, ...rightNodes, nodeMapping[currentId]]; +}; diff --git a/docs/caseShow/demos/dataflow/index.tsx b/docs/caseShow/demos/dataflow/index.tsx new file mode 100644 index 0000000..df2a7fc --- /dev/null +++ b/docs/caseShow/demos/dataflow/index.tsx @@ -0,0 +1,115 @@ +/** + * compact: true + */ +import { + FlowView, + FlowViewNode, + Inspector, + SelectType, + useEdgesState, + useNodesState, +} from '@ant-design/pro-flow'; +import { useCallback, useMemo, useState } from 'react'; +import { edges, nodes } from './data'; +import { + formatEdgeMapping, + formatNodeMapping, + getAllHighLightNode, + setEdgeDanger, + setNodeDanger, +} from './helper'; +import useStyles from './styled'; + +const ProFlowDemo = () => { + const { styles } = useStyles(); + const [current, setCurrent] = useState(0); + const [_nodes, setNodes, onNodesChange] = useNodesState([...nodes]); + const [_edges, setEdges, onEdgesChange] = useEdgesState([...edges]); + const edgeMapping = useMemo(() => { + const mapping = formatEdgeMapping(_nodes, _edges); + return setEdgeDanger(mapping); + }, [_nodes, _edges]); + const nodeMapping = useMemo(() => { + const mapping = formatNodeMapping(_nodes, _edges); + return setNodeDanger(mapping); + }, [_nodes, _edges]); + + const renderData = useMemo(() => { + return { + nodes: Object.values(nodeMapping), + edges: Object.values(edgeMapping), + }; + }, [nodeMapping, edgeMapping]); + + const handleHighlight = useCallback( + (node: FlowViewNode) => { + handleUnHighlight(); + setCurrent(node.id); + const highNodes = getAllHighLightNode(nodeMapping, node.id); + _nodes.forEach((_node) => { + if (highNodes.find((item) => item.id === _node.id)) { + _node.select = SelectType.SUB_SELECT; + } + if (_node.id === node.id) { + _node.select = SelectType.SELECT; + } + }); + + setNodes(_nodes); + setEdges( + edges.map((edge) => { + if ( + highNodes.find((item) => item.id === edge.source) && + highNodes.find((item) => item.id === edge.target) + ) { + edge.select = SelectType.SUB_SELECT; + } + return { + ...edge, + }; + }), + ); + }, + [nodeMapping], + ); + + const handleUnHighlight = () => { + setCurrent(0); + setNodes( + _nodes.map((_node) => { + _node.select = SelectType.DEFAULT; + return _node; + }), + ); + setEdges( + edges.map((edge) => { + edge.select = SelectType.DEFAULT; + return edge; + }), + ); + }; + + return ( +
+ handleHighlight(node)} + onPaneClick={handleUnHighlight} + nodes={renderData.nodes} + edges={renderData.edges} + onEdgesChange={onEdgesChange} + onNodesChange={onNodesChange} + stepLessZooming={true} + miniMap={false} + > + setCurrent(0)} + /> + +
+ ); +}; + +export default ProFlowDemo; diff --git a/docs/caseShow/demos/dataflow/styled.ts b/docs/caseShow/demos/dataflow/styled.ts new file mode 100644 index 0000000..4b0907d --- /dev/null +++ b/docs/caseShow/demos/dataflow/styled.ts @@ -0,0 +1,30 @@ +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(({ css }) => { + return { + dangerLogo: { + width: '28px', + height: '16px', + background: '#ffeef1', + borderRadius: '7px', + marginTop: '3px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + img: { width: '8px', height: '9px' }, + }, + container: css` + width: 100%; + height: 600px; + .ant-progress-text { + text-align: center !important; + } + `, + CustomWrap: { + width: '300px', + height: '100px', + backgroundColor: 'red', + }, + }; +}); +export default useStyles; diff --git a/docs/caseShow/demos/index.style.ts b/docs/caseShow/demos/index.style.ts index 1800c75..15cce1b 100644 --- a/docs/caseShow/demos/index.style.ts +++ b/docs/caseShow/demos/index.style.ts @@ -5,7 +5,7 @@ const useStyles = createStyles(() => { pipeNodeWrap: { width: '260px', minHeight: '100px', - backgroundColor: '#f6f8fa', + backgroundColor: 'white', padding: '16px', boxSizing: 'border-box', borderRadius: '8px', @@ -25,6 +25,7 @@ const useStyles = createStyles(() => { boxSizing: 'border-box', border: '1px solid rgba(0, 0, 0, 0.08)', borderRadius: '8px', + backgroundColor: 'white', }, mainBox: { width: '100%', diff --git a/docs/caseShow/demos/pipelineDemo.tsx b/docs/caseShow/demos/pipeline/multiPipe/data.ts similarity index 58% rename from docs/caseShow/demos/pipelineDemo.tsx rename to docs/caseShow/demos/pipeline/multiPipe/data.ts index b4df109..77ba116 100644 --- a/docs/caseShow/demos/pipelineDemo.tsx +++ b/docs/caseShow/demos/pipeline/multiPipe/data.ts @@ -1,103 +1,7 @@ -/** - * compact: true - */ -import { - Background, - FlowView, - FlowViewProvider, - Handle, - NodeProps, - Position, - SelectType, - useFlowViewer, -} from '@ant-design/pro-flow'; -import { FC, useCallback } from 'react'; -import useStyles from './index.style'; - -interface PipeNodeChild { - title: string; - des: string; - logo: string; -} - -interface PipeNode { - stepTitle: string; - title: string; - des: string; - logo: string; - needSwitch?: boolean; - children?: PipeNodeChild[]; - selectType?: SelectType; -} - const nodeWidth = 170; const nodeHeight = 500; -export const PipeNode: FC> = ({ data }) => { - const { stepTitle, title, des, logo, needSwitch = false, children = [], selectType } = data; - const { styles } = useStyles(); - - return ( -
- -
{stepTitle}
-
-
-
- -
-
-
{title}
-
{des}
-
- {needSwitch && ( -
-
-
-
-
- )} -
- {children.length > 0 && ( -
- {children.map((item, index) => ( -
-
- -
-
-
{item.title}
-
{item.des}
-
-
- ))} -
- )} -
- -
- ); -}; - -const nodeTypes = { pipeNode: PipeNode }; - -const nodes = [ +export const nodes = [ { id: 'a1', type: 'pipeNode', @@ -120,6 +24,8 @@ const nodes = [ title: 'NPM 组件初始化', logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original', des: '30秒', + leftHandle: false, + rightHandle: true, }, { title: '同步成员(仅子组件生...', @@ -127,7 +33,7 @@ const nodes = [ des: '0秒', }, { - title: '注册部署平台', + title: '注册部署', logo: 'https://mdn.alipayobjects.com/huamei_d2ejos/afts/img/A*sko9RoPu-HgAAAAAAAAAAAAADvl6AQ/original', des: '0秒', }, @@ -234,12 +140,14 @@ const nodes = [ }, ]; -const edges = [ +export const edges = [ { id: 'e1', source: 'a1', target: 'a2', animated: true, + sourceHandle: 'a1', + targetHandle: 'a2', }, { id: 'e2', @@ -266,44 +174,3 @@ const edges = [ animated: true, }, ]; - -function App() { - const flowViewer = useFlowViewer(); - const { styles } = useStyles(); - - const handleClick = useCallback( - (e, n) => { - flowViewer?.zoomToNode(n.id, 1000); - }, - [flowViewer], - ); - - const handlePaneClick = useCallback(() => { - // flowViewer?.zoomToNode(n.id, 1000); - }, [flowViewer]); - - return ( -
- - - -
- ); -} - -function ProApp() { - return ( - - - - ); -} - -export default ProApp; diff --git a/docs/caseShow/demos/pipeline/multiPipe/pipeNode.tsx b/docs/caseShow/demos/pipeline/multiPipe/pipeNode.tsx new file mode 100644 index 0000000..01f559f --- /dev/null +++ b/docs/caseShow/demos/pipeline/multiPipe/pipeNode.tsx @@ -0,0 +1,109 @@ +import { Handle, NodeProps, Position, SelectType } from '@ant-design/pro-flow'; +import { FC } from 'react'; +import useStyles from '../../index.style'; + +interface PipeNodeChild { + title: string; + des: string; + logo: string; +} + +interface PipeNode { + stepTitle: string; + title: string; + des: string; + logo: string; + needSwitch?: boolean; + children?: PipeNodeChild[]; + selectType?: SelectType; +} + +export const PipeNode: FC> = ({ id, data }) => { + const { stepTitle, title, des, logo, needSwitch = false, children = [], selectType } = data; + const { styles } = useStyles(); + + return ( +
+ +
{stepTitle}
+
+
+
+ +
+
+
{title}
+
{des}
+
+ {needSwitch && ( +
+
+
+
+
+ )} +
+ {children.length > 0 && ( +
+ {children.map((item, index) => ( +
+ {item.leftHandle && ( + + )} +
+
+ +
+
+
{item.title}
+
{item.des}
+
+
+ {item.rightHandle && ( + + )} +
+ ))} +
+ )} +
+ +
+ ); +}; diff --git a/docs/caseShow/demos/pipeline/multiPipe/pipelineDemo.tsx b/docs/caseShow/demos/pipeline/multiPipe/pipelineDemo.tsx new file mode 100644 index 0000000..782cdb1 --- /dev/null +++ b/docs/caseShow/demos/pipeline/multiPipe/pipelineDemo.tsx @@ -0,0 +1,57 @@ +/** + * compact: true + */ +import { + Background, + FlowView, + FlowViewProvider, + useEdgesState, + useFlowViewer, + useNodesState, +} from '@ant-design/pro-flow'; +import { useCallback } from 'react'; +import useStyles from '../../index.style'; +import { edges, nodes } from './data.ts'; +import { PipeNode } from './pipeNode'; + +const nodeTypes = { pipeNode: PipeNode }; + +function App() { + const flowViewer = useFlowViewer(); + const { styles } = useStyles(); + const [_nodes, , onNodesChange] = useNodesState([...nodes]); + const [_edges, , onEdgesChange] = useEdgesState([...edges]); + + const handleClick = useCallback( + (e, n) => { + flowViewer?.zoomToNode(n.id, 1000); + }, + [flowViewer], + ); + + return ( +
+ + + +
+ ); +} + +function ProApp() { + return ( + + + + ); +} + +export default ProApp; diff --git a/docs/caseShow/demos/pipeline/taskPipeline/data.tsx b/docs/caseShow/demos/pipeline/taskPipeline/data.tsx new file mode 100644 index 0000000..9101487 --- /dev/null +++ b/docs/caseShow/demos/pipeline/taskPipeline/data.tsx @@ -0,0 +1,223 @@ +import { Button } from 'antd'; +import { TaskContent } from './nodes/taskContent'; +export const ActionBtn = () => { + return ( + + ); +}; + +export const nodes = [ + { + id: 'A', + type: 'StageNode', + position: { x: 100, y: 100 }, + data: { + title: '配置校验', + extra:
+
, + children: [ + { + id: 'task1', + title: '提交申请', + status: 'success', + extra: , + isOpen: false, + children: , + }, + { + id: 'task2', + title: '提交申请123', + status: 'success', + extra: , + isOpen: false, + children: , + }, + ], + }, + zIndex: -1, + }, + { + id: 'B', + type: 'StageNode', + position: { x: 400, y: 100 }, + data: { + title: '多任务并行', + children: [ + { + id: 'b1', + title: '提交申请', + status: 'success', + isOpen: false, + }, + { + id: 'b2', + title: '提交申请', + status: 'success', + isOpen: true, + children: , + }, + { + id: 'b3', + title: '提交申请123', + status: 'success', + isOpen: false, + children: , + }, + ], + }, + zIndex: -1, + }, + { + id: 'B2', + type: 'StageNode', + position: { x: 400, y: 500 }, + data: { + title: '多任务并行', + children: [ + { + id: 'b21', + title: '提交申请', + status: 'success', + isOpen: false, + }, + { + id: 'b22', + title: '提交申请', + status: 'success', + isOpen: true, + children: , + }, + { + id: 'b23', + title: '提交申请123', + status: 'success', + isOpen: false, + children: , + }, + ], + }, + zIndex: -1, + }, + { + id: 'C', + type: 'StageNode', + position: { x: 700, y: 100 }, + data: { + title: '阶段名称', + children: [ + { + id: 'c1', + title: '提交申请', + status: 'success', + extra: , + isOpen: false, + children: , + }, + { + id: 'c2', + title: '提交申请123', + status: 'success', + extra: , + isOpen: false, + children: , + }, + ], + }, + zIndex: -1, + }, + { + id: 'D', + type: 'StageNode', + position: { x: 1000, y: 100 }, + data: { + title: '阶段名称', + children: [ + { + id: 'd1', + title: '提交申请', + status: 'success', + extra: , + isOpen: false, + children: , + }, + ], + }, + zIndex: -1, + }, +]; + +export const edges = [ + { + id: 'A-B', + source: 'A', + target: 'B', + sourceHandle: 'task1', + targetHandle: 'b2', + type: 'bezier', + }, + { + id: 'A-B2', + source: 'A', + target: 'B', + sourceHandle: 'task1', + type: 'bezier', + targetHandle: 'b3', + }, + { + id: 'A-B3', + source: 'A', + target: 'B2', + sourceHandle: 'task1', + type: 'bezier', + targetHandle: 'b23', + }, + { + id: 'B-C', + source: 'B', + target: 'C', + sourceHandle: 'b1', + type: 'bezier', + targetHandle: 'c1', + }, + { + id: 'B2-C', + source: 'B2', + target: 'C', + sourceHandle: 'b23', + type: 'bezier', + targetHandle: 'c1', + }, + { + id: 'B-C2', + source: 'B', + target: 'C', + sourceHandle: 'b2', + type: 'bezier', + targetHandle: 'c1', + }, + { + id: 'B-C3', + source: 'B', + target: 'C', + sourceHandle: 'b3', + type: 'bezier', + targetHandle: 'c2', + }, + { + id: 'C-D', + source: 'C', + target: 'D', + sourceHandle: 'c1', + type: 'bezier', + targetHandle: 'd1', + }, + { + id: 'C-D2', + source: 'C', + target: 'D', + sourceHandle: 'c2', + type: 'bezier', + targetHandle: 'd1', + }, +]; diff --git a/docs/caseShow/demos/pipeline/taskPipeline/index.tsx b/docs/caseShow/demos/pipeline/taskPipeline/index.tsx new file mode 100644 index 0000000..217809c --- /dev/null +++ b/docs/caseShow/demos/pipeline/taskPipeline/index.tsx @@ -0,0 +1,52 @@ +/** + * compact: true + */ + +import { Background, FlowView, applyEdgeChanges, applyNodeChanges } from '@ant-design/pro-flow'; +import { useCallback, useState } from 'react'; +import { edges, nodes } from './data'; +import StageNode from './nodes/stageNode'; +import taskNode from './nodes/taskNode'; +import useStyles from './styled'; + +const nodeTypes = { taskNode, StageNode }; + +function App() { + const { styles } = useStyles(); + const [_nodes, setNodes] = useState(nodes); + const [_edges, setEdges] = useState(edges); + + const onNodesChange = useCallback( + (changes) => setNodes((nds) => applyNodeChanges(changes, nds)), + [setNodes], + ); + + const onEdgesChange = useCallback( + (changes) => setEdges((eds) => applyEdgeChanges(changes, eds)), + [setEdges], + ); + + return ( +
+ + + +
+ ); +} + +function ProApp() { + return ; +} + +export default ProApp; diff --git a/docs/caseShow/demos/pipeline/taskPipeline/nodes/stageNode.tsx b/docs/caseShow/demos/pipeline/taskPipeline/nodes/stageNode.tsx new file mode 100644 index 0000000..01690f9 --- /dev/null +++ b/docs/caseShow/demos/pipeline/taskPipeline/nodes/stageNode.tsx @@ -0,0 +1,27 @@ +import { FlowViewNode } from '@ant-design/pro-flow'; +import { FC } from 'react'; +import useStyles from './styled'; +import TaskNode from './taskNode'; + +const StageNode: FC = (node) => { + // const [open, setOpen] = useState(false); + const { styles } = useStyles(); + const { data } = node; + const taskNodes = data.children; + + return ( +
+
+
{data.title}
+ {data.extra} +
+
+ {taskNodes.map((taskNode: any) => { + return ; + })} +
+
+ ); +}; + +export default StageNode; diff --git a/docs/caseShow/demos/pipeline/taskPipeline/nodes/styled.ts b/docs/caseShow/demos/pipeline/taskPipeline/nodes/styled.ts new file mode 100644 index 0000000..4ef49a3 --- /dev/null +++ b/docs/caseShow/demos/pipeline/taskPipeline/nodes/styled.ts @@ -0,0 +1,88 @@ +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(() => { + return { + wrap: { + width: '240px', + backgroundColor: '#FAFAFA', + marginBottom: '10px', + borderRadius: '8px', + position: 'relative', + }, + taskNode: { + width: '240px', + minHeight: '42px', + backgroundColor: 'white', + padding: '8px 12px', + borderRadius: '8px', + boxSizing: 'border-box', + border: '1px solid #e8e8e8', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + '.title': { + width: '100px', + marginLeft: '10px', + }, + '.group': { + width: '100px', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + + '.extra': { + width: '60px', + }, + + '.span': { + width: '20px', + display: 'block', + textAlign: 'center', + }, + }, + }, + + stageNode: { + width: '256px', + height: ' 100%', + backgroundColor: '#FAFAFA', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + borderRadius: '8px', + + '.wrap': { + width: '100%', + display: 'flex', + flexWrap: 'nowrap', + justifyContent: 'space-between', + padding: '10px', + }, + + '.title': { + width: '100px', + marginLeft: '10px', + }, + }, + + taskContent: { + margin: '0', + padding: '8px 12px', + listStyle: 'none', + border: '1px solid #f0f0f0', + borderTop: 'none', + '> li': { + display: 'flex', + alignItems: 'center', + marginBottom: '8px', + '> div': { + marginLeft: '8px', + display: 'flex', + justifyContent: 'space-between', + width: '100%', + }, + }, + }, + }; +}); +export default useStyles; diff --git a/docs/caseShow/demos/pipeline/taskPipeline/nodes/taskContent.tsx b/docs/caseShow/demos/pipeline/taskPipeline/nodes/taskContent.tsx new file mode 100644 index 0000000..c929160 --- /dev/null +++ b/docs/caseShow/demos/pipeline/taskPipeline/nodes/taskContent.tsx @@ -0,0 +1,43 @@ +import { green, red } from '@ant-design/colors'; +import { CheckCircleFilled, ClockCircleFilled, CloseCircleFilled } from '@ant-design/icons'; +import useStyles from './styled'; + +export const TaskContent = () => { + const { styles } = useStyles(); + + return ( +
    +
  • + +
    + 七尼尼 已完成 + 43s +
    +
  • +
  • +
  • + +
    + lydon 已完成 + 43s +
    +
  • +
  • +
  • + +
    + 青霓 审批不通过 + 43s +
    +
  • +
  • +
  • + +
    + 青霓 未开始 + +
    +
  • +
+ ); +}; diff --git a/docs/caseShow/demos/pipeline/taskPipeline/nodes/taskNode.tsx b/docs/caseShow/demos/pipeline/taskPipeline/nodes/taskNode.tsx new file mode 100644 index 0000000..476106d --- /dev/null +++ b/docs/caseShow/demos/pipeline/taskPipeline/nodes/taskNode.tsx @@ -0,0 +1,67 @@ +import { green } from '@ant-design/colors'; +import { CheckCircleFilled } from '@ant-design/icons'; +import { Handle, Position } from '@ant-design/pro-flow'; +import { useState } from 'react'; +import useStyles from './styled'; + +export interface TaskNodeC { + id: string; + title: string; + status: string; + extra: React.JSX.Element; + isOpen: boolean; + children: React.JSX.Element; +} + +const TaskNode = (props: { data: TaskNodeC }) => { + const { data } = props; + const [open, setOpen] = useState(data.isOpen); + const { styles } = useStyles(); + + return ( +
+ +
+ +
{data.title}
+ {data.children && ( +
+ {data.extra &&
{data.extra}
} + {data.extra && |} +
setOpen(!open)}>{open ? 'on' : 'off'}
+
+ )} +
+ {open && data.children} + +
+ ); +}; + +export default TaskNode; diff --git a/docs/caseShow/demos/techPipeLine.style.ts b/docs/caseShow/demos/pipeline/taskPipeline/styled.ts similarity index 100% rename from docs/caseShow/demos/techPipeLine.style.ts rename to docs/caseShow/demos/pipeline/taskPipeline/styled.ts diff --git a/docs/caseShow/demos/techPipeline.tsx b/docs/caseShow/demos/pipeline/techPipe/data.ts similarity index 58% rename from docs/caseShow/demos/techPipeline.tsx rename to docs/caseShow/demos/pipeline/techPipe/data.ts index 0a8257f..f63e3e6 100644 --- a/docs/caseShow/demos/techPipeline.tsx +++ b/docs/caseShow/demos/pipeline/techPipe/data.ts @@ -1,104 +1,8 @@ -/** - * compact: true - */ -import { - Background, - FlowView, - FlowViewProvider, - Handle, - Position, - useFlowViewer, -} from '@ant-design/pro-flow'; -import { FC, useCallback } from 'react'; -import useStyles from './techPipeLine.style'; - -interface PipeNodeChild { - title: string; - logo?: string; - id: string; -} - -interface PipeNode { - title: string; - logo: string; - des?: string; - children?: PipeNodeChild[]; -} - const nodeWidth = 250; const nodeHeight = 150; const edgeType = 'bezier'; -const PipeNode: FC<{ - data: PipeNode; -}> = ({ data }) => { - const { title, des, logo, children = [] } = data; - const { styles } = useStyles(); - - return ( -
-
-
-
- -
-
{title}
-
- -
-
- - {children.length > 0 && ( -
- {children.map((item, index) => ( - <> -
- - - {item.logo && ( -
- -
- )} - -
-
{item.title}
-
- -
- - ))} -
- )} - - {des &&
{des}
} -
-
- ); -}; - -const nodeTypes = { pipeNode: PipeNode }; - -const nodes = [ +export const nodes = [ { id: 'a1', type: 'pipeNode', @@ -198,7 +102,7 @@ const nodes = [ }, ]; -const edges = [ +export const edges = [ { id: 'edge-1', source: 'a1', @@ -296,43 +200,3 @@ const edges = [ targetHandle: 'a4-2-target', }, ]; - -function App() { - const flowViewer = useFlowViewer(); - const { styles } = useStyles(); - - const handleClick = useCallback( - (e, n) => { - flowViewer?.zoomToNode(n.id, 1000); - }, - [flowViewer], - ); - - const handlePaneClick = useCallback(() => { - // flowViewer?.zoomToNode(n.id, 1000); - }, [flowViewer]); - return ( -
- - - -
- ); -} - -function ProApp() { - return ( - - - - ); -} - -export default ProApp; diff --git a/docs/caseShow/demos/pipeline/techPipe/pipeNode.tsx b/docs/caseShow/demos/pipeline/techPipe/pipeNode.tsx new file mode 100644 index 0000000..a7db940 --- /dev/null +++ b/docs/caseShow/demos/pipeline/techPipe/pipeNode.tsx @@ -0,0 +1,84 @@ +import { Handle, Position } from '@ant-design/pro-flow'; +import useStyles from './techPipeLine.style'; + +interface PipeNodeChild { + title: string; + logo?: string; + id: string; +} + +interface PipeNode { + title: string; + logo: string; + des?: string; + children?: PipeNodeChild[]; +} + +const PipeNode: FC<{ + data: PipeNode; +}> = ({ data }) => { + const { title, des, logo, children = [] } = data; + const { styles } = useStyles(); + + return ( +
+
+
+
+ +
+
{title}
+
+ +
+
+ + {children.length > 0 && ( +
+ {children.map((item, index) => ( +
+
+ + + {item.logo && ( +
+ +
+ )} + +
+
{item.title}
+
+ +
+
+ ))} +
+ )} + + {des &&
{des}
} +
+
+ ); +}; + +export default PipeNode; diff --git a/docs/caseShow/demos/pipeline/techPipe/techPipeLine.style.ts b/docs/caseShow/demos/pipeline/techPipe/techPipeLine.style.ts new file mode 100644 index 0000000..592e1aa --- /dev/null +++ b/docs/caseShow/demos/pipeline/techPipe/techPipeLine.style.ts @@ -0,0 +1,93 @@ +import { createStyles } from 'antd-style'; + +const useStyles = createStyles(() => { + return { + techUIpipeNodeWrap: { + width: '260px', + minHeight: '100px', + backgroundColor: '#f6f8fa', + padding: '12px 6px', + boxSizing: 'border-box', + borderRadius: '8px', + }, + + pipeNode: { + width: '100%', + borderRadius: '4px', + backgroundColor: '#fff', + paddingBottom: '12px', + }, + mainBox: { + width: '100%', + padding: '12px', + height: '45px', + backgroundColor: 'white', + display: 'flex', + borderBottom: 'none', + borderRadius: '8px', + boxSizing: 'border-box', + position: 'relative', + }, + logo: { + display: 'flex', + alignItems: 'center', + img: { width: '16px', height: '16px' }, + }, + title: { + marginLeft: '8px', + fontSize: '14px', + fontWeight: '500', + color: '#000', + lineHeight: '22px', + whiteSpace: 'nowrap', + }, + lineTitle: { + marginLeft: '8px', + fontSize: '14px', + fontWeight: '500', + color: '#000', + lineHeight: '22px', + whiteSpace: 'nowrap', + }, + subLogo: { + position: 'absolute', + right: '6px', + top: '8px', + img: { width: '80px', height: '25px' }, + }, + des: { + marginTop: '16px', + color: '#00000073', + fontSize: '10px', + }, + children: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + paddingBottom: '8px', + }, + childrenBox: { + width: '220px', + height: '30px', + paddingLeft: '5px', + backgroundColor: 'white', + display: 'flex', + alignItems: 'center', + border: '1px solid #f1f1f1', + borderRadius: '3px', + boxSizing: 'border-box', + marginTop: '3px', + position: 'relative', + }, + wrap: { + marginLeft: '8px', + display: 'flex', + flexDirection: 'column', + }, + container: { + width: '100%', + height: '600px', + }, + }; +}); +export default useStyles; diff --git a/docs/caseShow/demos/pipeline/techPipe/techPipeline.tsx b/docs/caseShow/demos/pipeline/techPipe/techPipeline.tsx new file mode 100644 index 0000000..3062263 --- /dev/null +++ b/docs/caseShow/demos/pipeline/techPipe/techPipeline.tsx @@ -0,0 +1,58 @@ +/** + * compact: true + */ + +import { + Background, + FlowView, + FlowViewProvider, + useEdgesState, + useFlowViewer, + useNodesState, +} from '@ant-design/pro-flow'; +import { useCallback } from 'react'; +import { edges, nodes } from './data'; +import PipeNode from './pipeNode'; +import useStyles from './techPipeLine.style'; + +const nodeTypes = { pipeNode: PipeNode }; + +function App() { + const flowViewer = useFlowViewer(); + const { styles } = useStyles(); + const [_nodes, , onNodesChange] = useNodesState([...nodes]); + const [_edges, , onEdgesChange] = useEdgesState([...edges]); + + const handleClick = useCallback( + (e, n) => { + flowViewer?.zoomToNode(n.id, 1000); + }, + [flowViewer], + ); + + return ( +
+ + + +
+ ); +} + +function ProApp() { + return ( + + + + ); +} + +export default ProApp; diff --git a/docs/caseShow/demos/workflow/index.tsx b/docs/caseShow/demos/workflow/index.tsx new file mode 100644 index 0000000..c556d96 --- /dev/null +++ b/docs/caseShow/demos/workflow/index.tsx @@ -0,0 +1,106 @@ +/** + * compact: true + * defaultShowCode: true + */ +import { + BasicNode, + EditNode, + FlowEditor, + FlowEditorProvider, + useFlowEditor, +} from '@ant-design/pro-flow'; +import { useCallback, useEffect } from 'react'; +import { StringRender } from '/docs/guide/demos/flowEditor/StringNode'; + +import TextNode from './nodes/textNode'; +import Sidebar from './sidebar'; +import useStyles from './styled'; + +let id = 0; +const getId = () => `node_${id++}`; + +const nodeTypes = { + TextNode: TextNode, + StringNode: StringRender, + BasicNode: BasicNode, + EditNode: EditNode, +}; +const ProFlowDemo = () => { + const editor = useFlowEditor(); + const { styles } = useStyles(); + + const onDragOver = useCallback((event) => { + event.preventDefault(); + event.dataTransfer.dropEffect = 'move'; + }, []); + + const onDrop = useCallback( + (event) => { + event.preventDefault(); + if (!editor) return; + + const type = event.dataTransfer.getData('application/reactflow'); + if (typeof type === 'undefined' || !type) { + return; + } + + const position = editor.screenToFlowPosition({ + x: event.clientX, + y: event.clientY, + }); + const newNode = { + id: getId(), + type, + position, + content: { + a: '123', + }, + data: { + title: `${type} node`, + content: '123', + }, + }; + + editor.addNode(newNode); + }, + [editor], + ); + + useEffect(() => { + editor.addNode({ + id: 'a1', + type: 'TextNode', + position: { x: 200, y: 100 }, + data: { + title: '123', + aaa: '456', + }, + }); + }, [editor]); + + return ( +
+ + + +
+ ); +}; + +const FlowDemo = () => { + return ( + + + + ); +}; + +export default FlowDemo; diff --git a/docs/caseShow/demos/workflow/nodes/network.tsx b/docs/caseShow/demos/workflow/nodes/network.tsx new file mode 100644 index 0000000..dc887d1 --- /dev/null +++ b/docs/caseShow/demos/workflow/nodes/network.tsx @@ -0,0 +1,18 @@ +import { FC } from 'react'; +import useStyles from '../styled'; + +interface NetworkNode { + title: string; + logo: string; + des?: string; +} + +const NetworkNode: FC<{ + data: NetworkNode; +}> = ({ data }) => { + const { styles } = useStyles(); + + return
; +}; + +export default NetworkNode; diff --git a/docs/caseShow/demos/workflow/nodes/textNode.tsx b/docs/caseShow/demos/workflow/nodes/textNode.tsx new file mode 100644 index 0000000..b661462 --- /dev/null +++ b/docs/caseShow/demos/workflow/nodes/textNode.tsx @@ -0,0 +1,16 @@ +import { Handle, Position } from '@ant-design/pro-flow'; +import useStyles from '../styled'; + +const TextNode = () => { + const { styles } = useStyles(); + + return ( +
+

文本节点

+