| | |
| | | import React, { useRef, useEffect, useState } from "react"; |
| | | import { Button, message, Modal } from 'antd'; |
| | | import { Button, message, Modal, Divider, List, Typography, Input, Popconfirm } from 'antd'; |
| | | import { DeleteFilled, ApiOutlined } from '@ant-design/icons'; |
| | | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; |
| | | import { solarizedlight } from 'react-syntax-highlighter/dist/esm/styles/prism'; |
| | | import './GraphTools.less' |
| | | import { exportDataToServer, getFlowList, deleteFlowById, updateFlowStatus, mockRun } from "../../services/flow/api"; |
| | | import './css/GraphTools.less' |
| | | import { flow, remove } from "lodash"; |
| | | import { find } from "lodash"; |
| | | |
| | | export const GraphTools = ({ graphRef, isReady }) => { |
| | | |
| | | const [isModalOpen, setIsModalOpen] = useState(false); |
| | | const [saveIsModalOpen, setSaveIsModalOpen] = useState(false); |
| | | const [preCode, setPreCode] = useState(null); |
| | | const [flowListData, setFlowListData] = useState([]); |
| | | const [currentFlow, setCurrentFlow] = useState(null); |
| | | const [flowName, setFlowName] = useState(null); |
| | | const [flowMemo, setFlowMemo] = useState(null); |
| | | |
| | | let flowId = -1; |
| | | |
| | | const handleOk = () => { |
| | | setIsModalOpen(false); |
| | |
| | | |
| | | const handleCancel = () => { |
| | | setIsModalOpen(false); |
| | | setSaveIsModalOpen(false); |
| | | }; |
| | | |
| | | const flowNameInputChange = (e) => { |
| | | setFlowName(e.target.value) |
| | | } |
| | | |
| | | const memoInputChange = (e) => { |
| | | setFlowMemo(e.target.value) |
| | | } |
| | | |
| | | const saveData = () => { |
| | | if (currentFlow == null) { |
| | | flowId = -1; |
| | | } |
| | | |
| | | setSaveIsModalOpen(true); |
| | | } |
| | | |
| | | //预览代码 |
| | | const prewCode = () => { |
| | |
| | | return; |
| | | } |
| | | |
| | | console.log(getDescendants(rootNode, nodes, graph)); |
| | | let id = null; |
| | | if (currentFlow != null) { |
| | | id = currentFlow.id; |
| | | } |
| | | |
| | | const codeContent = transCode(rootNode, nodes, graph) |
| | | console.log(codeContent); |
| | | let result = sortNodes(rootNode, nodes, graph); |
| | | let targetResult = setResult(edges, result) |
| | | console.log(targetResult); |
| | | exportDataToServer({ |
| | | originData: JSON.stringify(data), |
| | | data: result, |
| | | name: flowName, |
| | | memo: flowMemo, |
| | | id: id |
| | | }).then((res) => { |
| | | if (res.code == 200) { |
| | | message.success('保存成功'); |
| | | updateFlowList(); |
| | | } else { |
| | | message.warning(res.msg); |
| | | } |
| | | setSaveIsModalOpen(false); |
| | | }) |
| | | } |
| | | } |
| | | |
| | | const transCode = (rootNode, nodes, graph) => { |
| | | let codeContent = ""; |
| | | const sortNodes = (rootNode, nodes, graph) => { |
| | | let values = nodeDFS(rootNode, nodes, graph); |
| | | const searchNode = { |
| | | id: 1, |
| | |
| | | }; |
| | | |
| | | let cpValues = JSON.parse(JSON.stringify(values)) |
| | | console.log(cpValues); |
| | | |
| | | let searchIndex = 0; |
| | | cpValues.forEach((value) => { |
| | |
| | | } |
| | | }) |
| | | |
| | | return cpValues; |
| | | } |
| | | |
| | | const setResult = (edges, result) => { |
| | | console.log(edges, result); |
| | | result.forEach((item) => { |
| | | let find = findEdge(edges, item.id) |
| | | |
| | | item.nextTrue = ""; |
| | | item.nextFalse = ""; |
| | | |
| | | find.forEach((edge) => { |
| | | if (edge.data.logicBool == true) { |
| | | item.nextTrue = edge.target.cell; |
| | | } else { |
| | | item.nextFalse = edge.target.cell; |
| | | } |
| | | }) |
| | | }) |
| | | |
| | | return result; |
| | | } |
| | | |
| | | const findEdge = (edges, parentId) => { |
| | | let list = []; |
| | | edges.forEach((edge) => { |
| | | if (edge.source.cell == parentId) { |
| | | list.push(edge); |
| | | } |
| | | }) |
| | | |
| | | return list; |
| | | } |
| | | |
| | | const transCode = (rootNode, nodes, graph) => { |
| | | let codeContent = ""; |
| | | |
| | | let values = nodeDFS(rootNode, nodes, graph); |
| | | const searchNode = { |
| | | id: 1, |
| | | parent: null, |
| | | logicBool: true |
| | | }; |
| | | |
| | | let cpValues = JSON.parse(JSON.stringify(values)) |
| | | |
| | | let searchIndex = 0; |
| | | cpValues.forEach((value) => { |
| | | if (value.data.isLogic) { |
| | | value.data.searchLogicId = searchNode.id; |
| | | value.data.searchLogicBool = searchNode.logicBool; |
| | | value.data.searchIndex = searchIndex++; |
| | | |
| | | let tmpSearchNode = JSON.parse(JSON.stringify(searchNode)) |
| | | searchNode.parent = tmpSearchNode; |
| | | searchNode.id = value.id; |
| | | searchNode.logicBool = null; |
| | | searchIndex = 0; |
| | | } else { |
| | | let id = searchNode.id; |
| | | let logicBool = searchNode.logicBool; |
| | | |
| | | const connectedEdges = graph.getConnectedEdges(value);//取边 |
| | | connectedEdges.forEach((edge) => { |
| | | let tmpSearchNode = JSON.parse(JSON.stringify(searchNode)); |
| | | while (tmpSearchNode.parent != null) { |
| | | if (edge.source.cell == tmpSearchNode.id) { |
| | | logicBool = edge.data.logicBool;//更新方向 |
| | | searchNode.logicBool = edge.data.logicBool; |
| | | id = tmpSearchNode.id; |
| | | break; |
| | | } |
| | | tmpSearchNode = tmpSearchNode.parent; |
| | | } |
| | | }) |
| | | |
| | | value.data.searchLogicId = id; |
| | | value.data.searchLogicBool = logicBool; |
| | | value.data.searchIndex = searchIndex++; |
| | | } |
| | | }) |
| | | console.log(cpValues); |
| | | console.log(searchNode); |
| | | |
| | |
| | | obj.id = key; |
| | | sortTmp[tmp[key].index] = obj; |
| | | } |
| | | |
| | | console.log(sortTmp); |
| | | |
| | | // 合并True和False |
| | | sortTmp.forEach((item) => { |
| | |
| | | return formattedCode; |
| | | } |
| | | |
| | | // const transCode = (rootNode, nodes, graph) => { |
| | | // let codeContent = ""; |
| | | // const descendants = []; |
| | | // let stack = [rootNode]; |
| | | |
| | | // let count = 0; |
| | | // while (stack.length > 0) { |
| | | // const current = stack.pop(); |
| | | // descendants.push(current); |
| | | |
| | | // const children = getChildren(current, nodes, graph); |
| | | // stack.push(...children); |
| | | |
| | | // // 输出代码 |
| | | // if (!current.data.isLogic) { |
| | | // const connectedEdges = graph.getConnectedEdges(current);//取边 |
| | | // connectedEdges.forEach((edge) => { |
| | | // //过滤从自身节点出去的边 |
| | | // if(edge.source.cell != current.id){ |
| | | // //取上一节点 |
| | | // let lastNode = null; |
| | | // nodes.forEach((node) => { |
| | | // if(node.id == edge.source.cell){ |
| | | // lastNode = node; |
| | | // } |
| | | // }) |
| | | |
| | | // if(lastNode != null) { |
| | | // //判断节点是否逻辑节点 |
| | | // if(lastNode.data.isLogic){ |
| | | // console.log(lastNode); |
| | | // let nestedIfCode = ""; |
| | | // if(lastNode.data.logicBool == 'true') { |
| | | // nestedIfCode = ` |
| | | // if (${lastNode.data.codeContent}) { |
| | | // ${current.data.codeContent} |
| | | // } |
| | | // `; |
| | | // }else{ |
| | | // nestedIfCode = ` |
| | | // if (!(${lastNode.data.codeContent})) { |
| | | // ${current.data.codeContent} |
| | | // } |
| | | // `; |
| | | // } |
| | | |
| | | |
| | | // codeContent += "\n" + nestedIfCode; |
| | | // console.log(codeContent); |
| | | // }else{ |
| | | // if (current.data.codeContent != null) { |
| | | // codeContent += "\n" + current.data.codeContent; |
| | | // } |
| | | // } |
| | | // } |
| | | // } |
| | | // console.log(current); |
| | | // }) |
| | | // } else { |
| | | // // if (current.data.codeContent != null) { |
| | | // // codeContent += "\n" + current.data.codeContent; |
| | | // // } |
| | | |
| | | // // const connectedEdges = graph.getConnectedEdges(current); |
| | | // // console.log(connectedEdges); |
| | | // // stack = [] |
| | | // // let test = [] |
| | | // // connectedEdges.forEach((edge) => { |
| | | // // nodes.forEach((item) => { |
| | | // // if (item.id === edge.target.cell && item.id != current.id) { |
| | | // // test.push(item); |
| | | // // } |
| | | // // }) |
| | | // // }); |
| | | // // console.log(test); |
| | | // // console.log(); |
| | | // // let nestedIfCode = ` |
| | | // // if (true}) { |
| | | // // ${current.data.codeContent} |
| | | // // } |
| | | // // `; |
| | | |
| | | // // codeContent += "\n" + nestedIfCode; |
| | | // // console.log(codeContent); |
| | | // } |
| | | |
| | | // } |
| | | |
| | | // console.log(codeContent); |
| | | // } |
| | | |
| | | const nodeDFS = (node, nodes, graph) => { |
| | | let values = []; |
| | | if (graph) { |
| | | const connectedEdges = graph.getConnectedEdges(node); |
| | | const children = []; |
| | | |
| | | console.log(node); |
| | | // console.log(node); |
| | | connectedEdges.forEach((edge) => { |
| | | nodes.forEach((item) => { |
| | | if (item.id === edge.target.cell && item.id != node.id) { |
| | |
| | | }) |
| | | }); |
| | | |
| | | console.log(connectedEdges); |
| | | // console.log(connectedEdges); |
| | | if (children.length != 0) { |
| | | console.log(children); |
| | | // console.log(children); |
| | | children.forEach((node) => { |
| | | console.log(node); |
| | | // console.log(node); |
| | | values.push(node); |
| | | values = values.concat(nodeDFS(node, nodes, graph)) |
| | | }) |
| | |
| | | return values; |
| | | } |
| | | |
| | | const getChildren = (node, nodes, graph) => { |
| | | const connectedEdges = graph.getConnectedEdges(node); |
| | | const children = []; |
| | | |
| | | connectedEdges.forEach((edge) => { |
| | | nodes.forEach((item) => { |
| | | if (item.id === edge.target.cell && item.id != node.id) { |
| | | children.push(item); |
| | | } |
| | | }) |
| | | }); |
| | | |
| | | return children; |
| | | } |
| | | |
| | | const getDescendants = (node, nodes, graph) => { |
| | | const descendants = []; |
| | | const stack = [node]; |
| | | |
| | | let count = 0; |
| | | while (stack.length > 0) { |
| | | const current = stack.pop(); |
| | | descendants.push(current); |
| | | |
| | | const children = getChildren(current, nodes, graph); |
| | | stack.push(...children); |
| | | const setFlowActive = () => { |
| | | if (currentFlow == null) { |
| | | message.warning("请选择要激活使用的流程图!"); |
| | | return; |
| | | } |
| | | |
| | | return descendants; |
| | | const status = currentFlow.status == 1 ? 0 : 1; |
| | | updateFlowStatus(currentFlow.id, status).then((res) => { |
| | | if (res.code == 200) { |
| | | message.success(status == 1 ? "激活成功" : "已取消激活"); |
| | | currentFlow.status = status; |
| | | } else { |
| | | message.warning(res.msg); |
| | | } |
| | | updateFlowList(); |
| | | }) |
| | | } |
| | | |
| | | const removeFlow = () => { |
| | | if (currentFlow == null) { |
| | | message.warning("请选择要删除的流程图!"); |
| | | return; |
| | | } |
| | | |
| | | deleteFlowById(currentFlow.id).then((res) => { |
| | | if (res.code == 200) { |
| | | message.success("删除成功"); |
| | | } else { |
| | | message.warning(res.msg); |
| | | } |
| | | updateFlowList(); |
| | | }) |
| | | } |
| | | |
| | | const updateFlowList = () => { |
| | | getFlowList().then((res) => { |
| | | setFlowListData(res.data); |
| | | }) |
| | | } |
| | | |
| | | const createNewBlank = () => { |
| | | const graph = graphRef.current; |
| | | if (graph) { |
| | | graph.clearCells(); |
| | | setCurrentFlow(null); |
| | | setFlowName(null); |
| | | setFlowMemo(null); |
| | | } |
| | | } |
| | | |
| | | const switchFlowBlank = (flow) => { |
| | | const graph = graphRef.current; |
| | | if (graph) { |
| | | graph.fromJSON(JSON.parse(flow.originData)); |
| | | setCurrentFlow(flow) |
| | | setFlowName(flow.name); |
| | | setFlowMemo(flow.memo); |
| | | } |
| | | } |
| | | |
| | | const testRun = () => {//模拟运行 |
| | | if (currentFlow == null) { |
| | | message.warning("请选择流程图"); |
| | | return; |
| | | } |
| | | |
| | | mockRun(currentFlow.id).then((res) => { |
| | | if (res.code == 200) { |
| | | message.success("运行成功"); |
| | | } else { |
| | | message.warning(res.msg); |
| | | } |
| | | }) |
| | | } |
| | | |
| | | useEffect(() => { |
| | | updateFlowList(); |
| | | }, []) |
| | | |
| | | return ( |
| | | <> |
| | | <div className="container"> |
| | | <Button type="primary" onClick={exportData}> |
| | | 导出数据 |
| | | </Button> |
| | | <div> |
| | | <div className="container"> |
| | | <div className="containerButton"> |
| | | <Button type="primary" onClick={createNewBlank}> |
| | | 新建 |
| | | </Button> |
| | | </div> |
| | | |
| | | <Button type="primary" onClick={prewCode}> |
| | | 预览代码 |
| | | </Button> |
| | | <div className="containerButton"> |
| | | <Button type="primary" onClick={saveData}> |
| | | 保存 |
| | | </Button> |
| | | </div> |
| | | |
| | | <div className="containerButton"> |
| | | <Button type="primary" onClick={setFlowActive}> |
| | | 激活使用 |
| | | </Button> |
| | | </div> |
| | | |
| | | <div className="containerButton"> |
| | | <Button type="primary" onClick={prewCode}> |
| | | 预览代码 |
| | | </Button> |
| | | </div> |
| | | |
| | | <div className="containerButton"> |
| | | <Button type="primary" onClick={testRun}> |
| | | 模拟运行 |
| | | </Button> |
| | | </div> |
| | | </div> |
| | | |
| | | <div className="flowList"> |
| | | <List |
| | | header={ |
| | | <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> |
| | | <div><Typography.Text mark>[数据]</Typography.Text> 流程图列表</div> |
| | | <div> |
| | | <Button type="primary" size="small" onClick={updateFlowList}> |
| | | 刷新 |
| | | </Button> |
| | | |
| | | <Popconfirm |
| | | title="删除流程图" |
| | | description="确认删除?" |
| | | okText="删除" |
| | | cancelText="取消" |
| | | onConfirm={removeFlow} |
| | | > |
| | | <Button style={{ marginLeft: '5px' }} type="primary" danger size="small"> |
| | | <DeleteFilled /> |
| | | </Button> |
| | | </Popconfirm> |
| | | |
| | | </div> |
| | | </div> |
| | | } |
| | | dataSource={flowListData} |
| | | renderItem={(item) => ( |
| | | <List.Item> |
| | | <Button type={currentFlow != null && item.id == currentFlow.id ? 'primary' : 'dashed'} style={{ width: '100%' }} onClick={() => switchFlowBlank(item)}> |
| | | <div style={{ display: 'flex', justifyContent: 'space-between' }}> |
| | | <div>{item.name}</div> |
| | | {item.status == 1 ? <div><ApiOutlined /></div> : ''} |
| | | </div> |
| | | </Button> |
| | | </List.Item> |
| | | )} |
| | | /> |
| | | </div> |
| | | </div> |
| | | |
| | | <Modal title="Basic Modal" open={isModalOpen} onOk={handleOk} onCancel={handleCancel}> |
| | | <Modal title="预览代码" open={isModalOpen} onOk={handleOk} onCancel={handleCancel}> |
| | | <SyntaxHighlighter language="java" style={solarizedlight}> |
| | | {preCode} |
| | | </SyntaxHighlighter> |
| | | </Modal> |
| | | |
| | | <Modal title="保存流程图" open={saveIsModalOpen} onOk={exportData} onCancel={handleCancel}> |
| | | <div style={{ marginTop: '10px' }}> |
| | | <Input placeholder="流程图名称" value={flowName} onChange={flowNameInputChange} /> |
| | | </div> |
| | | <div style={{ marginTop: '10px' }}> |
| | | <Input placeholder="备注" value={flowMemo} onChange={memoInputChange} /> |
| | | </div> |
| | | </Modal> |
| | | </> |
| | | ); |
| | | } |