luxiaotao1123
2024-04-08 9d0acfb65c80c4948c305ca01338f894b87346a0
zy-asrs-flow/src/components/Flow/GraphTools.jsx
@@ -1,15 +1,583 @@
import React, { useRef, useEffect } from "react";
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 }) => {
export const GraphTools = ({ graphRef, isReady }) => {
    const exportData = () => {
    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 (graph) {
        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;
            }
            const codeContent = transCode(rootNode, nodes, graph)
            console.log(codeContent);
            setPreCode(codeContent);
            setIsModalOpen(true);
        }
    }
    return <button onClick={exportData}>导出数据</button>;
    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>
        </>
    );
}