import React, { useRef, useEffect, useState } from "react"; 
 | 
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 { exportDataToServer, getFlowList, deleteFlowById, updateFlowStatus, mockRun } from "../../services/flow/api"; 
 | 
import './css/GraphTools.less' 
 | 
import { flow, remove } 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 = () => { 
 | 
        const graph = graphRef.current; 
 | 
        if (isReady) { 
 | 
            const data = graph.toJSON(); 
 | 
  
 | 
            const edges = []; 
 | 
            const nodes = []; 
 | 
            let rootNode = null; 
 | 
            data.cells.forEach((item) => { 
 | 
                if (item.shape == "edge") { 
 | 
                    edges.push(item) 
 | 
                } else { 
 | 
                    nodes.push(item) 
 | 
                    if (item.data.root) { 
 | 
                        rootNode = item; 
 | 
                    } 
 | 
                } 
 | 
            }) 
 | 
  
 | 
            if (rootNode == null) { 
 | 
                message.warning('请设置程序入口组件'); 
 | 
                return; 
 | 
            } 
 | 
  
 | 
            const codeContent = transCode(rootNode, nodes, graph) 
 | 
            console.log(codeContent); 
 | 
  
 | 
            setPreCode(codeContent); 
 | 
            setIsModalOpen(true); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    const exportData = () => { 
 | 
        const graph = graphRef.current; 
 | 
        if (isReady) { 
 | 
            const data = graph.toJSON(); 
 | 
            console.log(data); 
 | 
            // 这里你可以将数据发送到服务器或保存到本地 
 | 
  
 | 
            const edges = []; 
 | 
            const nodes = []; 
 | 
            let rootNode = null; 
 | 
            data.cells.forEach((item) => { 
 | 
                if (item.shape == "edge") { 
 | 
                    edges.push(item) 
 | 
                } else { 
 | 
                    nodes.push(item) 
 | 
                    if (item.data.root) { 
 | 
                        rootNode = item; 
 | 
                    } 
 | 
                } 
 | 
            }) 
 | 
  
 | 
            if (rootNode == null) { 
 | 
                message.warning('请设置程序入口组件'); 
 | 
                return; 
 | 
            } 
 | 
  
 | 
            let id = null; 
 | 
            if (currentFlow != null) { 
 | 
                id = currentFlow.id; 
 | 
            } 
 | 
  
 | 
            let result = sortNodes(rootNode, nodes, graph); 
 | 
            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 sortNodes = (rootNode, nodes, graph) => { 
 | 
        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++; 
 | 
            } 
 | 
        }) 
 | 
  
 | 
        return cpValues; 
 | 
    } 
 | 
  
 | 
    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); 
 | 
  
 | 
        let tmp = {} 
 | 
        let tmpList = [] 
 | 
        let tmpIndex = 0; 
 | 
        for (let i = cpValues.length - 1; i >= 0; i--) { 
 | 
            let item = cpValues[i]; 
 | 
            if (tmp[item.data.searchLogicId] == null) { 
 | 
                tmpList[tmpIndex] = [item]; 
 | 
                tmp[item.data.searchLogicId] = { 
 | 
                    index: tmpIndex, 
 | 
                    code: "", 
 | 
                    codeTrue: "", 
 | 
                    codeFalse: "", 
 | 
                    condition: "" 
 | 
                }; 
 | 
                tmpIndex++; 
 | 
            } else { 
 | 
                tmpList[tmp[item.data.searchLogicId].index].push(item); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        console.log(tmp, tmpList); 
 | 
  
 | 
        tmpList.forEach((item) => { 
 | 
            item.forEach((val) => { 
 | 
                let originCode = tmp[val.data.searchLogicId].codeTrue; 
 | 
                if (!val.data.searchLogicBool) { 
 | 
                    originCode = tmp[val.data.searchLogicId].codeFalse; 
 | 
                } 
 | 
  
 | 
                let codeContent = val.data.codeContent; 
 | 
  
 | 
                if (val.data.isLogic) { 
 | 
                    codeContent = val.id + "_logic_tag"; 
 | 
                    console.log(val.data); 
 | 
                    tmp[val.id].condition = val.data.codeContent; 
 | 
                } else { 
 | 
                    codeContent = ` 
 | 
                    //**********${val.attrs.text.text}-start**********// 
 | 
                    ${codeContent} 
 | 
                    //**********${val.attrs.text.text}-start**********// 
 | 
                    `; 
 | 
                } 
 | 
                let newCode = ` 
 | 
                    ${codeContent} 
 | 
  
 | 
                    ${originCode} 
 | 
                `; 
 | 
  
 | 
                console.log(newCode); 
 | 
                if (val.data.searchLogicBool) { 
 | 
                    tmp[val.data.searchLogicId].codeTrue = newCode; 
 | 
                } else { 
 | 
                    tmp[val.data.searchLogicId].codeFalse = newCode; 
 | 
                } 
 | 
            }) 
 | 
        }) 
 | 
  
 | 
        let sortTmp = []; 
 | 
        for (var key in tmp) { 
 | 
            let obj = tmp[key]; 
 | 
            obj.id = key; 
 | 
            sortTmp[tmp[key].index] = obj; 
 | 
        } 
 | 
  
 | 
        console.log(sortTmp); 
 | 
  
 | 
        // 合并True和False 
 | 
        sortTmp.forEach((item) => { 
 | 
            let nestedIfCode = ""; 
 | 
            if (item.condition == "") { 
 | 
                nestedIfCode = ` 
 | 
                ${item.codeTrue} 
 | 
                ${item.codeFalse} 
 | 
                `; 
 | 
            } else { 
 | 
                nestedIfCode = ` 
 | 
                if(${item.condition}){ 
 | 
                    // 逻辑TRUE 
 | 
                    ${item.codeTrue} 
 | 
                }else { 
 | 
                    // 逻辑FALSE 
 | 
                    ${item.codeFalse} 
 | 
                } 
 | 
            `; 
 | 
            } 
 | 
  
 | 
            item.code = nestedIfCode; 
 | 
        }) 
 | 
  
 | 
        console.log(sortTmp); 
 | 
  
 | 
        let finalTmp = {}; 
 | 
        let sortTmpCopy = JSON.parse(JSON.stringify(sortTmp)); 
 | 
        sortTmpCopy.forEach((item) => { 
 | 
            if (item.id != "1") { 
 | 
                let codeContent = item.code; 
 | 
                sortTmp.forEach((val) => { 
 | 
                    codeContent = codeContent.replace(val.id + "_logic_tag", val.code); 
 | 
                    console.log(item, val.id, codeContent); 
 | 
                }) 
 | 
                finalTmp[item.id] = { 
 | 
                    code: codeContent 
 | 
                } 
 | 
            } 
 | 
        }) 
 | 
  
 | 
        console.log(sortTmpCopy); 
 | 
        console.log(finalTmp); 
 | 
  
 | 
        sortTmpCopy.forEach((item) => { 
 | 
            if (item.id == "1") { 
 | 
                let finalCode = item.code; 
 | 
                for (var key in finalTmp) { 
 | 
                    let obj = finalTmp[key]; 
 | 
                    finalCode = finalCode.replace(key + "_logic_tag", obj.code); 
 | 
                } 
 | 
  
 | 
                codeContent = finalCode; 
 | 
            } 
 | 
        }) 
 | 
  
 | 
        codeContent = formatJavaCode(codeContent) 
 | 
        return codeContent; 
 | 
    } 
 | 
  
 | 
    const formatJavaCode = (codeString) => { 
 | 
        let baseIndentation = "    ";  //用四个空格表示一个缩进 
 | 
        let indentationLevel = 0;  //增加这行代码来初始化indentationLevel 
 | 
  
 | 
        let formattedCode = codeString 
 | 
            .replace(/^\s+/mg, '') // 移除每行前面的空白 
 | 
            .replace(/(\{|\})/g, ' $& ') // 让大括号周围都有空格 
 | 
            // 上面的.replace(/^\s+/mg, '')可能会在括号周围插入多余的空格,所以下面这行代码会移除开头或末尾的空格 
 | 
            .replace(/^\s+|\s+$/mg, '') 
 | 
            // 用了.split('\n')后,每一行都是数组中的一个元素,所以可以通过减少或增加行开头的空格数来添加或删除缩进 
 | 
            .split('\n').reduce((formattedCode, currentLine) => { 
 | 
                if (currentLine.includes('}')) { 
 | 
                    // 如果一行中包含右大括号,我们要减少一个缩进 
 | 
                    indentationLevel--; 
 | 
                } 
 | 
  
 | 
                let indentation = baseIndentation.repeat(indentationLevel); 
 | 
                let indentedLine = indentation + currentLine; 
 | 
  
 | 
                if (currentLine.includes('{')) { 
 | 
                    // 如果一行中包含左大括号,那个大括号后面的代码需要额外的一个缩进 
 | 
                    indentationLevel++; 
 | 
                } 
 | 
  
 | 
                return formattedCode + '\n' + indentedLine; 
 | 
            }, ''); 
 | 
  
 | 
        return formattedCode; 
 | 
    } 
 | 
  
 | 
    const nodeDFS = (node, nodes, graph) => { 
 | 
        let values = []; 
 | 
        if (graph) { 
 | 
            const connectedEdges = graph.getConnectedEdges(node); 
 | 
            const children = []; 
 | 
  
 | 
            // console.log(node); 
 | 
            connectedEdges.forEach((edge) => { 
 | 
                nodes.forEach((item) => { 
 | 
                    if (item.id === edge.target.cell && item.id != node.id) { 
 | 
                        children.push(item); 
 | 
                    } 
 | 
                }) 
 | 
            }); 
 | 
  
 | 
            // console.log(connectedEdges); 
 | 
            if (children.length != 0) { 
 | 
                // console.log(children); 
 | 
                children.forEach((node) => { 
 | 
                    // console.log(node); 
 | 
                    values.push(node); 
 | 
                    values = values.concat(nodeDFS(node, nodes, graph)) 
 | 
                }) 
 | 
            } 
 | 
        } 
 | 
  
 | 
        return values; 
 | 
    } 
 | 
  
 | 
    const setFlowActive = () => { 
 | 
        if (currentFlow == null) { 
 | 
            message.warning("请选择要激活使用的流程图!"); 
 | 
            return; 
 | 
        } 
 | 
  
 | 
        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> 
 | 
                <div className="container"> 
 | 
                    <div className="containerButton"> 
 | 
                        <Button type="primary" onClick={createNewBlank}> 
 | 
                            新建 
 | 
                        </Button> 
 | 
                    </div> 
 | 
  
 | 
                    <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="预览代码" 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> 
 | 
        </> 
 | 
    ); 
 | 
} 
 |