react-flow实现dag工作流
1. 官方文档
2.效果
3. 代码
index.jsx
-
import { useState, useCallback, useEffect } from 'react';
-
import ReactFlow, {
-
Controls,
-
Background,
-
applyNodeChanges,
-
applyEdgeChanges,
-
addEdge,
-
ReactFlowProvider,
-
useReactFlow
-
} from 'reactflow';
-
import 'reactflow/dist/style.css';
-
import { TaskNode } from './taskNode';
-
import { Button, Space, message } from 'antd';
-
import { saveMonthTaskInfo, getMonthTaskInfo } from '@/api/modules/month_task_dag';
-
import AddNode from './addNodeForm';
-
import { store } from "@/redux";
-
-
// const initialNodes = [
-
// {
-
// id: 'qb_value_cal',
-
// data: { label: 'XXXX', task_status: 'success' },
-
// position: { x: 196.99037808813034, y: -16.15861389212995 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'platform_acct_price',
-
// data: { label: 'xxxx', task_status: 'failure' },
-
// position: { x: 9.004772768299915, y: 71.58667201646344 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 't_syn_settlement',
-
// data: { label: 'xxxx', task_status: 'failure' },
-
// position: { x: 198.74746953729812, y: 150.0741352155024 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'midas_sett',
-
// data: { label: 'xxxx', task_status: 'running' },
-
// position: { x: -145.96731433103253, y: -13.672818469718123 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'midas_sett_consume',
-
// data: { label: 'xxxx', task_status: 'failure' },
-
// position: { x: -123.70028149684317, y: 184.60354995136538 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'dyb_value_cal',
-
// data: { label: 'xxxx', task_status: 'running' },
-
// position: { x: 16.548987140345645, y: 268.97166003745264 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'prepay_dyb',
-
// data: { label: 'xxxxx', task_status: 'failure' },
-
// position: { x: 114.87245269459243, y: 393.62735077521256 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'prepay_consume',
-
// data: { label: 'xxxxx', task_status: 'failure' },
-
// position: { x: 114.87245269459243, y: 393.62735077521256 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'midas_dyb_price',
-
// data: { label: 'xxxxxxx', task_status: 'failure' },
-
// position: { x: 114.87245269459243, y: 393.62735077521256 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'midas_split',
-
// data: { label: 'xxxxxx', task_status: 'running' },
-
// position: { x: -107.1137248521369, y: 391.20277175226386 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'qb_value_adjust',
-
// data: { label: 'xxxxxxx', task_status: 'failure' },
-
// position: { x: 14.93930727104339, y: 499.4122832380474 },
-
// type: 'taskNode'
-
// },
-
// {
-
// id: 'midas_bill',
-
// data: { label: 'xxxxxxx', task_status: 'failure' },
-
// position: { x: 14.93930727104339, y: 499.4122832380474 },
-
// type: 'taskNode'
-
// }
-
// ];
-
// const initialEdges = [
-
// {
-
// id: "reactflow__edge-dyb_value_calb-p",
-
// source: "dyb_value_cal",
-
// sourcehAndle: "b",
-
// target: "prepay_dyb",
-
// targethAndle: "a"
-
// },
-
// {
-
// id: "reactflow__edge-midas_dyb_priceb",
-
// source: "midas_dyb_price",
-
// sourcehAndle: "b",
-
// target: "midas_split",
-
// targethAndle: "a"
-
// },
-
// {
-
// id: "reactflow__edge-midas_settb-plat",
-
// source: "midas_sett",
-
// sourcehAndle: "b",
-
// target: "platform_acct_price",
-
// targethAndle: "a"
-
// },
-
// {
-
// id: "reactflow__edge-midas_sett_consu",
-
// source: "midas_sett_consume",
-
// sourcehAndle: "b",
-
// target: "midas_dyb_price",
-
// targethAndle: "a"
-
// },
-
// {
-
// id: "reactflow__edge-platform_acct_pr",
-
// source: "platform_acct_price",
-
// sourcehAndle: "b",
-
// target: "dyb_value_cal",
-
// targethAndle: "a"
-
// },
-
// {
-
// id: "reactflow__edge-platform_acct_priceb-midas_sett_consumea",
-
// source: "platform_acct_price",
-
// sourcehAndle: "b",
-
// target: "midas_sett_consume",
-
// targethAndle: "a"
-
// },
-
// {
-
// id: "reactflow__edge-prepay_dybb-prep",
-
// source: "prepay_dyb",
-
// sourcehAndle: "b",
-
// target: "prepay_consume",
-
// targethAndle: "a"
-
// },
-
// {
-
// id: "reactflow__edge-qb_value_calb-qb",
-
// source: "qb_value_cal",
-
// sourcehAndle: "b",
-
// target: "qb_value_adjust",
-
// targethAndle: "a"
-
// },
-
// {
-
// id: "reactflow__edge-qb_value_calb-t_",
-
// source: "qb_value_cal",
-
// sourcehAndle: "b",
-
// target: "t_syn_settlement",
-
// targethAndle: "a"
-
// },
-
// {
-
// id: "reactflow__edge-t_syn_settlement",
-
// source: "t_syn_settlement",
-
// sourcehAndle: "b",
-
// target: "dyb_value_cal",
-
// targethAndle: "a"
-
// }
-
// ];
-
const nodeTypes = { taskNode: TaskNode };
-
const username = store.getState().global.userName
-
function Flow() {
-
const [nodes, setNodes] = useState([]);
-
const [edges, setEdges] = useState([]);
-
const handleGetNodesInfo = async () => {
-
const {data} = await getMonthTaskInfo({"module_view":"toc_revenue"})
-
console.log(data);
-
setNodes(data["initialNodes"]);
-
setEdges(data["initialEdges"]);
-
};
-
// 初始化是获取节点和边信息并渲染
-
useEffect(() => {
-
handleGetNodesInfo();
-
}, []);
-
// 获取flow实例
-
const reactFlowInstance = useReactFlow();
-
// 保存节点和边信息
-
const handleSaveNodes = async () => {
-
console.log(reactFlowInstance.getNodes())
-
console.log(reactFlowInstance.getEdges())
-
const {data} = await saveMonthTaskInfo({"initialNodes":reactFlowInstance.getNodes(), "initialEdges":reactFlowInstance.getEdges(), "module_view":"toc_revenue"})
-
console.log(data)
-
if (data=="success"){
-
message.success(data);
-
}else{
-
message.error(data);
-
}
-
};
-
// 添加节点
-
const handleAddNode = (newNode) => {
-
// setNodes([...nodes, newNode]); // 受控组件的添加方式
-
reactFlowInstance.addNodes(newNode) // 非受控组件的添加方式
-
};
-
const onNodesChange = useCallback(
-
// 这段代码使用了React的useCallback hook来创建一个函数onNodesChange,
-
// 其中用到了applyNodeChanges函数和setNodes函数。onNodesChange函数接收一个名为changes的参数,
-
// 这个参数表示节点的变化。applyNodeChanges函数根据这个变化来更新节点信息,并返回新的节点信息。
-
// setNodes函数则用来更新组件的状态,将新的节点信息更新到组件中。
-
// 最后,上述代码中的useCallback hook的第二个参数是一个依赖数组,表示只有当setNodes函数发生变化时,
-
// 才需要重新创建onNodesChange函数。这样可以避免不必要的重复渲染。
-
// applyNodeChanges
-
// 描述:返回具有应用更改的节点数组
-
// 类型:(changes: NodeChange[], nodes: Node[]) => Node[]
-
(changes) => setNodes((nds) => applyNodeChanges(changes, nds)),
-
[]
-
);
-
const onEdgesChange = useCallback((changes) => setEdges((eds) => applyEdgeChanges(changes, eds)),[]);
-
-
const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), []);
-
return (
-
<>
-
<Space direction="vertical">
-
<Space wrap>
-
<AddNode username={username} handleAddNode={handleAddNode}></AddNode>
-
<Button size="large" onClick={handleSaveNodes} >保存</Button>
-
</Space>
-
</Space>
-
<div style={{ height: '100%' }}>
-
<ReactFlow
-
nodes={nodes}
-
onNodesChange={onNodesChange}
-
edges={edges}
-
onEdgesChange={onEdgesChange}
-
onConnect={onConnect}
-
nodeTypes={nodeTypes}
-
>
-
<Background />
-
<Controls />
-
</ReactFlow>
-
</div>
-
</>
-
);
-
}
-
export default function () {
-
return (
-
<ReactFlowProvider>
-
<Flow />
-
</ReactFlowProvider>
-
);
-
}
taskNode.jsx
-
/* eslint-disable react/prop-types */
-
import React, { useState } from 'react'
-
import { Button, Modal, Form, Input, Popover, Table, message } from 'antd'
-
import { CheckCircleOutlined, CloseCircleOutlined, LoadingOutlined, PlayCircleOutlined } from '@ant-design/icons';
-
import { getMonthTaskInfo, getHistoryTaskInfo, runMonthTask } from '@/api/modules/month_task_dag';
-
import { store } from "@/redux";
-
import { getFifteenAgoData } from '@/utils/date_utils';
-
import { Handle, Position, useReactFlow } from 'reactflow';
-
const username = store.getState().global.userName
-
-
const columns = [
-
{
-
title: '任务ID',
-
dataIndex: 'task_type',
-
key: 'task_type',
-
},
-
{
-
title: '任务名称',
-
dataIndex: 'task_type_name',
-
key: 'task_type_name',
-
},
-
{
-
title: '任务状态',
-
dataIndex: 'task_status',
-
key: 'task_status',
-
},
-
{
-
title: '运行者',
-
dataIndex: 'user',
-
key: 'user',
-
},
-
{
-
title: '数据月份',
-
dataIndex: 'data_month',
-
key: 'data_month',
-
},
-
{
-
title: '创建时间',
-
dataIndex: 'create_time',
-
key: 'create_time',
-
},
-
{
-
title: '完成时间',
-
dataIndex: 'finish_time',
-
key: 'finish_time',
-
},
-
{
-
title: '修改时间',
-
dataIndex: 'modify_time',
-
key: 'modify_time',
-
}
-
];
-
-
// const historydata = [
-
// {
-
// task_id: '任务ID',
-
// task_name: '任务名称',
-
// task_status: '任务状态',
-
// user: '运行者',
-
// create_time:'创建时间',
-
// finish_time:'完成时间',
-
// modify_time:'修改时间'
-
// }
-
// ];
-
// eslint-disable-next-line react/prop-types
-
export function TaskNode({ data, isConnectable }) {
-
const [isModalVisible, setIsModalVisible] = useState(false); // 表单
-
const [isPopoverVisible, setPopoverVisible] = useState(false); //菜单
-
const [isTableVisible, setTableVisible] = useState(false); // 表格
-
const [historyRunData, setHistoryRunData] = useState(false); // 表格
-
-
const reactFlowInstance = useReactFlow();
-
-
const handleVisibleChange = (isPopoverVisible) => {
-
setPopoverVisible(isPopoverVisible);
-
};
-
const getHistoryInfo = async () => {
-
console.log(data)
-
const {data: historydata} = await getHistoryTaskInfo({"task_type":data.task_type})
-
setHistoryRunData(historydata)
-
};
-
const handleMenuClick = () => {
-
getHistoryInfo();
-
setPopoverVisible(false);
-
setTableVisible(true);
-
};
-
const menu = (
-
<a key="record" onClick={handleMenuClick}>
-
运行记录
-
</a>
-
);
-
-
const fifteenAgo = getFifteenAgoData()
-
-
const handleFormSubmit = (values) => {
-
const runTask = async () => {
-
const {data: runResult} = await runMonthTask({"task_type":data.task_type,"task_type_name":data.task_type_name, "data_month":values.data_month, "username":values.username})
-
console.log(runResult);
-
message.success(runResult);
-
};
-
// 提交任务后刷新任务状态
-
console.log(values, data.task_type, data.task_type_name);
-
runTask();
-
setIsModalVisible(false);
-
const handleGetNodesInfo = async () => {
-
const {data} = await getMonthTaskInfo({"module_view":"toc_revenue"})
-
console.log(data);
-
};
-
handleGetNodesInfo();
-
reactFlowInstance.addNodes[data["initialNodes"]];
-
};
-
const handleClick = () => {
-
setIsModalVisible(true);
-
};
-
-
const getStatusIcon = () => {
-
switch (data.task_status) {
-
case 'no_run':
-
return <PlayCircleOutlined />
-
case 'success':
-
return <CheckCircleOutlined />;
-
case 'failed':
-
return <CloseCircleOutlined />;
-
case 'running':
-
return <LoadingOutlined />;
-
default:
-
return null;
-
}
-
};
-
return (
-
<>
-
<Popover
-
content={menu} // 弹出一个菜单项
-
trigger="hover"
-
visible={isPopoverVisible}
-
onVisibleChange={handleVisibleChange}
-
placement="rightTop"
-
title="编辑选项"
-
>
-
<div style={{ display: 'flex', alignItems: 'center', padding: '10px', borderRadius: '10px', backgroundColor: '#f5f5f5' }}
-
onClick={handleClick}
-
>
-
<div style={{ marginRight: '10px', borderRadius: '50%', overflow: 'hidden' }}>
-
{getStatusIcon(data.task_status)}
-
</div>
-
<div>{data.task_type_name}</div>
-
<Handle type="target" position={Position.Top} id="a" isConnectable={isConnectable} />
-
<Handle type="source" position={Position.Bottom} id="b" isConnectable={isConnectable} />
-
</div>
-
</Popover>
-
{/* ===========================================运行记录=========================================== */}
-
<Modal
-
title="运行记录"
-
visible={isTableVisible}
-
onCancel={() => setTableVisible(false)}
-
footer={null}
-
width={'100vh'} // 设置Modal的宽度
-
>
-
<div style={{ overflowY: 'auto', maxHeight: '70vh' }}> {/* 设置带有垂直滚动条的容器 */}
-
<Table columns={columns} dataSource={historyRunData} rowKey="create_time" pagination={{ pageSize: 5 }}/>
-
</div>
-
</Modal>
-
{/* ===========================================表单=========================================== */}
-
<Modal
-
title={data.task_type_name}
-
visible={isModalVisible}
-
onCancel={() => setIsModalVisible(false)}
-
footer={null}
-
>
-
<Form onFinish={handleFormSubmit} initialValues={{ data_month:fifteenAgo.substring(0,7), username: username}}>
-
<Form.Item
-
label="数据月份"
-
name="data_month"
-
rules={[{ required: true, message: 'YYYY-MM' }]}
-
>
-
<Input />
-
</Form.Item>
-
<Form.Item
-
label="执行人"
-
name="username"
-
rules={[{ required: true }]}
-
>
-
<Input disabled/>
-
</Form.Item>
-
<Form.Item>
-
<Button htmlType="submit">运行</Button>
-
</Form.Item>
-
</Form>
-
</Modal>
-
</>
-
);
-
}
addNodeForm.jsx
-
import { PlusOutlined } from '@ant-design/icons';
-
import {
-
ModalForm,
-
ProFormText
-
} from '@ant-design/pro-components';
-
import { Button, message } from 'antd';
-
const AddNodeForm = (props:any) => {
-
const initialValues = {
-
mail_to: ''
-
};
-
return (
-
<ModalForm<{
-
task_type: string;
-
task_type_name: string;
-
system_module: string;
-
settlement_mode: string;
-
mail_to: string;
-
creater: string;
-
}
-
>
-
title="新增节点"
-
initialValues={initialValues}
-
trigger={
-
<Button type="primary" size="large">
-
<PlusOutlined />
-
新增节点
-
</Button>
-
}
-
autoFocusFirstInput
-
modalProps={{
-
onCancel: () => message.info("取消成功"),
-
}}
-
submitTimeout={2000}
-
onFinish={
-
async (values) => {
-
const newNode = {
-
id: values.task_type,
-
data: { task_type:values.task_type, task_type_name: values.task_type_name,
-
system_module:values.system_module, settlement_mode:values.settlement_mode,
-
mail_to: values.mail_to, creater:values.creater,
-
task_status: 'no_run'},
-
position: { x: Math.random() * 500, y: Math.random() * 500 },
-
type: 'taskNode'
-
}
-
props.handleAddNode(newNode)
-
message.success('提交成功');
-
return true;
-
}}
-
>
-
<ProFormText width="md" name="task_type" label="任务ID" placeholder="任务用于唯一标识的英文名" />
-
<ProFormText width="md" name="task_type_name" label="任务描述" placeholder="用于节点名称显示" />
-
<ProFormText width="md" name="system_module" label="归属的系统模块" placeholder="归属的系统模块" />
-
<ProFormText width="md" name="settlement_mode" label="归属的结算模式" placeholder="mobile/non_mobile/operator等" />
-
<ProFormText width="md" name="mail_to" label="邮件人" placeholder="邮件人" />
-
<ProFormText width="md" name="creater" label="创建人" disabled initialValue={props.username} />
-
</ModalForm>
-
);
-
};
-
-
export default AddNodeForm;
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgfgahi
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01