一个轻量级的库react-flow-renderer,领导让做一个拖拉拽的流程图,实现一个产品的制作流程。
样子大致如下:
image.png
一开始想用antv/x6,但是之前没用过,看了半天发现展示数据容易,想要实现节点的增删,拖拉拽,节点的先后顺序更改交互实在是无力。
然后发现了react-flow-renderer,详情可以查看:
[https://github.com/wbkd/react-flow]
[https://reactflow.dev/docs/introduction/]
实现的效果如下:
image.png
下面上代码:(reacthook+ts)
/**
* @description react-flow-renderer
* @author njj
*/
import ProForm, {
ProFormInstance, ProFormRadio, ProFormSelect, ProFormText, ProFormTextArea,
ProFormSwitch, ProFormUploadButton
} from "@ant-design/pro-form";
import { Button, Card, Divider, message, Tag, Input, Tooltip, Upload, Modal } from "antd";
import { useRef, useState, useEffect, useCallback } from "react";
import { ProductionPlanList } from '@/defind/plan';
import { getUrlParmas } from '@/common/utils/util';
import '../index.less';
import ReactFlow, {
MiniMap, Controls, applyNodeChanges,
addEdge, applyEdgeChanges,
} from 'react-flow-renderer';
import Item from "antd/lib/list/Item";
import WorkProcedureSelectModal from '@/pages/Plan/ProductionPlan/WorkProcedureSelectModal/index'
import { nodeObject } from '@/defind/plan'
import { history } from "umi";
export type ProductionFlowChartProps = {
onNodesCallback: (a: any, b: any) => void;
onNodeClickCallback:(object:objectStatment)=>void;
}
type objectStatment = {
id?: string|number;
data?: {
label: string | undefined;
};
position?: {
x: number;
y: number;
};
sourcePosition?: string;
targetPosition?: string;
connectable?: boolean;
}
const ProductionFlowChart: React.FC<ProductionFlowChartProps> = (props) => {
const [isRefresh, setIsRefresh] = useState(false);
const [nodesData, setNodesData] = useState([]) //节点数组
const [edgesData, setEdgesData] = useState([]) //边数组
const [choosedData, setChoosedData] = useState<objectStatment>({}) //选中的节点信息
const [inputInfo, setInputInfo] = useState() //Modal里输入的值
const [modalVisible, setModalVisible] = useState(false) //Modal是否显示
//////
const [nodes, setNodes] = useState<any[]|undefined>([]);
const [edges, setEdges] = useState([]);
const addNewFlow = (dataSource: any|undefined) => {
if (dataSource.length == 1) {
//如果是单个添加
let index = nodes?.length
//更新节点数组
let object1:objectStatment = {
id: index + '',
data: {
label: dataSource[0].name,
// id: dataSource[0].id,
...dataSource[0] //非必要好像,是否只要保存id和name就行?
},
// data: dataSource[0],
position: { x: 40 + index! * 250, y: 50 },
sourcePosition: 'right',
targetPosition: 'left',
connectable: true
}
let temp1 = nodes
temp1!.push(object1)
// console.log("======最终的nodes单个", temp1)
setNodes(temp1)
//重新渲染flow
setIsRefresh(true);
} else {
let temp1 = nodes
//多个一次性添加
dataSource?.map((item:object, index:number) => {
let index0 = temp1?.length
let object1 = {
id: index0 + '',
data: {
label: dataSource[index].name,
// id:dataSource[index].id
...dataSource[index] //非必要好像,是否只要保存id和name就行?
},
position: { x: 40 + index0! * 250, y: 50 },
sourcePosition: 'right',
targetPosition: 'left',
connectable: true
}
temp1!.push(object1)
})
// console.log("======最终的nodes多个", temp1)
setNodes(temp1)
//重新渲染flow
setIsRefresh(true);
}
}
useEffect(() => {
isRefresh && setTimeout(() => setIsRefresh(false));
}, [isRefresh]);
useEffect(() => {
//回传节点,路径信息信息
props.onNodesCallback(nodes, edges)
}, [nodes, edges]);
const onNodesChange = useCallback(
(changes) => {
//@ts-ignore
setNodes((nds) => applyNodeChanges(changes, nds))
},
[setNodes]
);
const onEdgesChange = useCallback(
(changes) => {
//@ts-ignore
setEdges((eds) => applyEdgeChanges(changes, eds))
},
[setEdges]
);
//连接节点时触发
const onConnect = useCallback(
(connection) => {
//@ts-ignore
setEdges((eds) => addEdge(connection, eds))
},
[setEdges]
);
const onNodeClick=(event: React.MouseEvent, node: objectStatment)=>{
console.log("=====onNodeClick",event,node)
setChoosedData(node)
props.onNodeClickCallback(node)
}
//删除节点
const deleteNode=()=>{
console.log("========nodes",nodes)
console.log("========choosedData",choosedData)
let temp = nodes
temp?.map((item,index)=>{
if(item.id == choosedData.id){
temp?.splice(index,1)
return
}
})
console.log("========temp",temp)
setNodes(temp)
//重新渲染flow
setIsRefresh(true);
}
return (
<div style={{ display: 'flex' }}>
<div style={{ width: 1200, height: 300 }}>
{!isRefresh &&
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
fitView
className="react-flow__edge"
//@ts-ignore
onNodeClick={onNodeClick}
/>
}
</div>
<div className='buttonBlocks'>
{/* <div>被选中的节点为{choosedData?.data?.label}</div> */}
<Button type="primary" onClick={() => {
setModalVisible(true)
setInputInfo(undefined)
}}>添加工序</Button>
<Button onClick={() => {
deleteNode()
}}>删除工序</Button>
<Button onClick={()=>{
if(JSON.stringify(choosedData) !== "{}"){
//@ts-ignore
history.push('/plan/productionPlan/add?id='+ choosedData?.data?.id)
}else{
message.info('请先选中工序')
}
}}>编辑工序参数</Button>
</div>
<div>
<WorkProcedureSelectModal
width={800}
visible={modalVisible}
onSubmit={(selectedRowKeys, dataSource:nodeObject[]|undefined) => {
// console.log("====selectedRowKeys, dataSource", selectedRowKeys, dataSource)
setModalVisible(false)
addNewFlow(dataSource)
}}
onCancel={() => { setModalVisible(false) }}
/>
</div>
</div>
)
}
export default ProductionFlowChart