mac
5 months ago
4 changed files with 151 additions and 1 deletions
@ -0,0 +1,53 @@ |
|||||
|
import dagre from 'dagre'; |
||||
|
const edgeType = 'smoothstep'; |
||||
|
|
||||
|
export const flattenTree = (tree) => { |
||||
|
let stack = [tree]; |
||||
|
let nodes = []; |
||||
|
let edges = []; |
||||
|
while (stack.length > 0) { |
||||
|
let node = stack.pop(); |
||||
|
nodes.push({ id: node.id, data: { ...node, label: `${node.name}` }, position: node.position }); |
||||
|
if (node.children && node.children.length > 0) { |
||||
|
node.children.map(item => { |
||||
|
edges.push({ source: node.id, target: item.id, id: item.id, type: edgeType, animated: true }); |
||||
|
stack.unshift(item); |
||||
|
}); |
||||
|
}; |
||||
|
}; |
||||
|
return { nodes, edges }; |
||||
|
}; |
||||
|
|
||||
|
export const getLayoutedElements = (nodes, edges, direction = 'TB') => { |
||||
|
const dagreGraph = new dagre.graphlib.Graph(); |
||||
|
dagreGraph.setDefaultEdgeLabel(() => ({})); |
||||
|
|
||||
|
const nodeWidth = 172; |
||||
|
const nodeHeight = 36; |
||||
|
const isHorizontal = direction === 'LR'; |
||||
|
dagreGraph.setGraph({ rankdir: direction }); |
||||
|
|
||||
|
nodes.forEach((node: any) => { |
||||
|
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight }); |
||||
|
}); |
||||
|
|
||||
|
edges.forEach((edge: any) => { |
||||
|
dagreGraph.setEdge(edge.source, edge.target); |
||||
|
}); |
||||
|
|
||||
|
dagre.layout(dagreGraph); |
||||
|
|
||||
|
nodes.forEach((node: any) => { |
||||
|
const nodeWithPosition = dagreGraph.node(node.id); |
||||
|
node.targetPosition = isHorizontal ? 'left' : 'top'; |
||||
|
node.sourcePosition = isHorizontal ? 'right' : 'bottom'; |
||||
|
node.position = { |
||||
|
x: nodeWithPosition.x - nodeWidth / 2, |
||||
|
y: nodeWithPosition.y - nodeHeight / 2, |
||||
|
}; |
||||
|
|
||||
|
return node; |
||||
|
}); |
||||
|
|
||||
|
return { nodes, edges }; |
||||
|
}; |
@ -0,0 +1,84 @@ |
|||||
|
import React, { useCallback, FC, useEffect, useState, useRef } from 'react'; |
||||
|
import ReactFlow, { |
||||
|
addEdge, |
||||
|
ConnectionLineType, |
||||
|
useNodesState, |
||||
|
useEdgesState, |
||||
|
Controls |
||||
|
} from 'react-flow-renderer'; |
||||
|
import { getLayoutedElements, flattenTree } from './data'; |
||||
|
import { Button, Form, Input, Modal, notification } from 'antd'; |
||||
|
import api from '@/api'; |
||||
|
|
||||
|
const InviteChart: FC = () => { |
||||
|
const [nodes, setNodes, onNodesChange] = useNodesState([]); |
||||
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]); |
||||
|
const [form] = Form.useForm() |
||||
|
const buttonRef = useRef(null) |
||||
|
|
||||
|
// 获取节点
|
||||
|
const getNodes = async (email?: string) => { |
||||
|
const res: any = await api.user_relation({ email: email || '' }); |
||||
|
if (res.code === 0) { |
||||
|
let { nodes, edges } = flattenTree(res.data) // 处理节点及连接线
|
||||
|
let { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(nodes, edges)//计算节点位置
|
||||
|
setNodes([...layoutedNodes]) |
||||
|
setEdges([...layoutedEdges]) |
||||
|
buttonRef.current.click() |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
const onFinish = (values) => { |
||||
|
form.resetFields() |
||||
|
getNodes(values.email) |
||||
|
} |
||||
|
|
||||
|
const onLayout = useCallback( |
||||
|
(direction: any) => { |
||||
|
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements( |
||||
|
nodes, |
||||
|
edges, |
||||
|
direction |
||||
|
); |
||||
|
|
||||
|
setNodes([...layoutedNodes]); |
||||
|
setEdges([...layoutedEdges]); |
||||
|
}, |
||||
|
[nodes, edges] |
||||
|
); |
||||
|
|
||||
|
|
||||
|
useEffect(() => { |
||||
|
getNodes(); |
||||
|
}, []); |
||||
|
|
||||
|
return ( |
||||
|
<div> |
||||
|
<Button onClick={() => onLayout('LR')} style={{ display: 'none' }} ref={buttonRef}>123123123</Button> |
||||
|
<Form layout='inline' form={form} onFinish={onFinish}> |
||||
|
<Form.Item name="email"> |
||||
|
<Input placeholder='请输入邮箱' style={{ width: 300 }} /> |
||||
|
</Form.Item> |
||||
|
<Form.Item> |
||||
|
<Button htmlType='submit' type='primary'>搜索</Button> |
||||
|
</Form.Item> |
||||
|
</Form> |
||||
|
|
||||
|
<div style={{ width: '100%', height: window.innerHeight - 165 }}> |
||||
|
<ReactFlow |
||||
|
nodes={nodes} |
||||
|
edges={edges} |
||||
|
onNodesChange={onNodesChange} |
||||
|
onEdgesChange={onEdgesChange} |
||||
|
connectionLineType={ConnectionLineType.SmoothStep} |
||||
|
fitView |
||||
|
> |
||||
|
<Controls /> |
||||
|
</ReactFlow> |
||||
|
</div> |
||||
|
|
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default InviteChart; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue