TestCaseDetail
import { useCallback, useMemo, useRef, useState } from 'react';
import ReactFlow, { addEdge, Background, Controls, MarkerType, ReactFlowProvider } from 'reactflow';
import 'reactflow/dist/style.css';
import ConditionNode from './customNode/ConditionNode';
import EndNode from './customNode/EndNode';
import PreStepNode from './customNode/PreStepNode';
import SqlNode from './customNode/SqlNode';
import StartNode from './customNode/StartNode';
import SubProcessNode from './customNode/SubProcessNode';
import TestCaseNode from './customNode/TestCaseNode';
import './index.css';
import Sidebar from './Sidebar';
const TestCaseDetail = (props) => {
const nodeTypes = useMemo(
() => ({
TestCaseNode: TestCaseNode,
StartNode: StartNode,
EndNode: EndNode,
ConditionNode: ConditionNode,
SubProcessNode: SubProcessNode,
PreStepNode: PreStepNode,
SqlNode: SqlNode,
}),
[],
);
const reactFlowWrapper = useRef(null);
const [reactFlowInstance, setReactFlowInstance] = useState(null);
const onConnect = useCallback((params) => {
const markerEnd = {
type: MarkerType.ArrowClosed,
};
params.markerEnd = markerEnd;
params.type = 'smoothstep';
props.setEdges((eds) => addEdge(params, eds));
}, []);
const onDragOver = useCallback((event) => {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
}, []);
const onDrop = useCallback(
(event) => {
event.preventDefault();
const reactFlowBounds = reactFlowWrapper.current.getBoundingClientRect();
const type = event.dataTransfer.getData('application/reactflow');
if (typeof type === 'undefined' || !type) {
return;
}
const position = reactFlowInstance.project({
x: event.clientX - reactFlowBounds.left,
y: event.clientY - reactFlowBounds.top,
});
const newNode = {
id: Date.now().toString(),
type,
position,
data: { label: `${type} node` },
};
props.setNodes((nds) => nds.concat(newNode));
},
[reactFlowInstance],
);
const onNodeClick = (e, node) => {
console.log('点击节点', node);
};
const onEdgeClick = (e, edge) => {
};
const changeNode = (val) => {
props.setNodes((nds) =>
nds.map((item) => {
if (item.id === val.id) {
item.data = val;
item.hidden = val.isHidden;
item.style = { background: val.nodeBg };
}
return item;
}),
);
};
const changeEdge = (val) => {
props.setEdges((nds) =>
nds.map((item) => {
if (item.id === val.id) {
item.label = val.label;
item.type = val.type;
item.style = { stroke: val.color };
}
return item;
}),
);
};
return (
<div className="dndflow" style={{ height: '95%' }}>
<ReactFlowProvider>
<Sidebar />
<div className="reactflow-wrapper" ref={reactFlowWrapper}>
<ReactFlow
nodes={props.nodes}
edges={props.edges}
nodeTypes={nodeTypes}
onNodesChange={props.onNodesChange}
onEdgesChange={props.onEdgesChange}
onNodeClick={onNodeClick}
onEdgeClick={onEdgeClick}
onConnect={onConnect}
onInit={setReactFlowInstance}
onDrop={onDrop}
onDragOver={onDragOver}
fitView
>
<Controls />
<Background variant="dots" gap={12} size={1} />
</ReactFlow>
</div>
</ReactFlowProvider>
</div>
);
};
export default TestCaseDetail;
Sidebar.jsx
const Sidebar = () => {
const onDragStart = (event, nodeType) => {
event.dataTransfer.setData('application/reactflow', nodeType);
event.dataTransfer.effectAllowed = 'move';
};
return (
<aside style={{ height: '90%' }}>
<div
style={{
backgroundColor: 'white',
color: 'black',
textAlign: 'center',
borderColor: 'black',
borderStyle: 'solid',
borderBottomLeftRadius: '2em',
borderBottomRightRadius: '2em',
borderTopLeftRadius: '2em',
borderTopRightRadius: '2em',
}}
className="dndnode input"
onDragStart={(event) => {
event.stopPropagation();
onDragStart(event, 'StartNode');
}}
draggable
>
开始
</div>
<div
style={{ borderStyle: 'dashed' }}
className="dndnode testcase"
onDragStart={(event) => {
event.stopPropagation();
onDragStart(event, 'PreStepNode');
}}
draggable
>
前置步骤
</div>
<div
className="dndnode testcase"
onDragStart={(event) => {
event.stopPropagation();
onDragStart(event, 'TestCaseNode');
}}
draggable
>
用例节点
</div>
<div
style={{
backgroundColor: 'white',
color: 'black',
textAlign: 'center',
borderColor: 'black',
borderStyle: 'solid',
borderWidth: 1,
borderLeftWidth: 8,
borderRightWidth: 8,
}}
className="dndnode testcase"
onDragStart={(event) => {
event.stopPropagation();
onDragStart(event, 'SubProcessNode');
}}
draggable
>
子流程
</div>
<div
style={{
backgroundColor: 'white',
color: 'black',
textAlign: 'center',
borderColor: 'black',
borderStyle: 'solid',
borderWidth: 4,
borderLeftWidth: 1,
borderRightWidth: 1,
}}
className="dndnode testcase"
onDragStart={(event) => {
event.stopPropagation();
onDragStart(event, 'SqlNode');
}}
draggable
>
SQL
</div>
<div
style={{
backgroundColor: 'gray',
color: 'black',
textAlign: 'center',
borderColor: 'gray',
borderStyle: 'solid',
borderWidth: 1,
clipPath: 'polygon(50% 0, 100% 50%, 50% 100%, 0 50%)',
transition: '1s clip-path',
}}
className="dndnode testcase"
onDragStart={(event) => {
event.stopPropagation();
onDragStart(event, 'ConditionNode');
}}
draggable
>
条件节点
</div>
<div
style={{
backgroundColor: 'white',
color: 'black',
textAlign: 'center',
borderColor: 'black',
borderStyle: 'solid',
borderBottomLeftRadius: '2em',
borderBottomRightRadius: '2em',
borderTopLeftRadius: '2em',
borderTopRightRadius: '2em',
}}
className="dndnode output"
onDragStart={(event) => {
event.stopPropagation();
onDragStart(event, 'EndNode');
}}
draggable
>
结束
</div>
</aside>
);
};
export default Sidebar;
customNode
import { Button } from 'antd';
import { Handle, Position } from 'reactflow';
const ConditionNode = ({ data }) => {
const label = data.label === undefined ? '新用例节点' : data.label;
const color = data.color === undefined ? 'white' : data.color;
const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;
return (
<div>
<Button
style={{
height: 40,
width: 250,
backgroundColor: color,
color: 'black',
textAlign: 'center',
borderColor: borderColor,
borderStyle: 'solid',
borderWidth: 3,
clipPath: 'polygon(50% 0, 100% 50%, 50% 100%, 0 50%)',
transition: '1s clip-path',
}}
>
{label}
</Button>
<Handle type="target" position={Position.Top} id="1" />
<Handle type="target" position={Position.Left} id="2" />
<Handle type="target" position={Position.Right} id="3" />
<Handle type="source" position={Position.Bottom} id="4" />
</div>
);
};
export default ConditionNode;
import { Button } from 'antd';
import { Handle, Position } from 'reactflow';
const EndNode = ({ data }) => {
const color = data.color === undefined ? 'white' : data.color;
const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;
return (
<div>
<Button
style={{
height: 40,
width: 150,
backgroundColor: color,
color: 'black',
textAlign: 'center',
borderColor: borderColor,
borderStyle: 'solid',
borderBottomLeftRadius: '2em',
borderBottomRightRadius: '2em',
borderTopLeftRadius: '2em',
borderTopRightRadius: '2em',
}}
>
结束
</Button>
<Handle type="target" position={Position.Top} id="1" />
</div>
);
};
export default EndNode;
import { Button } from 'antd';
import { Handle, Position } from 'reactflow';
const PreStepNode = ({ data }) => {
const label = data.label === undefined ? '前置步骤节点' : data.label;
const color = data.color === undefined ? 'white' : data.color;
const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;
return (
<div>
<Button
style={{
height: 40,
width: 250,
backgroundColor: color,
color: 'black',
textAlign: 'center',
borderColor: borderColor,
borderStyle: 'dashed',
}}
>
{label}
</Button>
<Handle type="target" position={Position.Top} id="1" />
<Handle type="source" position={Position.Left} id="2" />
<Handle type="source" position={Position.Right} id="3" />
<Handle type="source" position={Position.Bottom} id="4" />
</div>
);
};
export default PreStepNode;
import { Button } from 'antd';
import { Handle, Position } from 'reactflow';
const SqlNode = ({ data }) => {
const label = data.label === undefined ? 'SQL' : data.label;
const color = data.color === undefined ? 'white' : data.color;
const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;
return (
<div>
<Button
style={{
height: 40,
width: 250,
backgroundColor: color,
color: 'black',
textAlign: 'center',
borderColor: borderColor,
borderStyle: 'solid',
borderWidth: 4,
borderLeftWidth: 1,
borderRightWidth: 1,
}}
>
{label}
</Button>
<Handle type="target" position={Position.Top} id="1" />
<Handle type="source" position={Position.Left} id="2" />
<Handle type="source" position={Position.Right} id="3" />
<Handle type="source" position={Position.Bottom} id="4" />
</div>
);
};
export default SqlNode;
import { Button } from 'antd';
import { Handle, Position } from 'reactflow';
const StartNode = ({ data }) => {
const color = data.color === undefined ? 'white' : data.color;
const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;
return (
<div>
<Button
style={{
height: 40,
width: 150,
backgroundColor: color,
color: 'black',
textAlign: 'center',
borderColor: borderColor,
borderStyle: 'solid',
borderBottomLeftRadius: '2em',
borderBottomRightRadius: '2em',
borderTopLeftRadius: '2em',
borderTopRightRadius: '2em',
}}
>
开始
</Button>
<Handle type="source" position={Position.Bottom} id="4" />
</div>
);
};
export default StartNode;
import { Button } from 'antd';
import { Handle, Position } from 'reactflow';
const SubProcessNode = ({ data }) => {
const label = data.label === undefined ? '子流程' : data.label;
const color = data.color === undefined ? 'white' : data.color;
const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;
return (
<div>
<Button
style={{
height: 40,
width: 250,
backgroundColor: color,
color: 'black',
textAlign: 'center',
borderColor: borderColor,
borderStyle: 'solid',
borderWidth: 2,
borderLeftWidth: 8,
borderRightWidth: 8,
}}
>
{label}
</Button>
<Handle type="target" position={Position.Top} id="1" />
<Handle type="source" position={Position.Left} id="2" />
<Handle type="source" position={Position.Right} id="3" />
<Handle type="source" position={Position.Bottom} id="4" />
</div>
);
};
export default SubProcessNode;
import { Button } from 'antd';
import { Handle, Position } from 'reactflow';
const TestCaseNode = ({ data }) => {
const label = data.label === undefined ? '新用例节点' : data.label;
const color = data.color === undefined ? 'white' : data.color;
const borderColor = data.borderColor === undefined ? 'black' : data.borderColor;
return (
<div>
<Button
style={{
height: 40,
width: 250,
backgroundColor: color,
color: 'black',
textAlign: 'center',
borderColor: borderColor,
borderStyle: 'solid',
}}
>
{label}
</Button>
<Handle type="target" position={Position.Top} id="1" />
<Handle type="source" position={Position.Left} id="2" />
<Handle type="source" position={Position.Right} id="3" />
<Handle type="source" position={Position.Bottom} id="4" />
</div>
);
};
export default TestCaseNode;