jointjs3 -- 加强小demo

根据传进来的数据,生成相应的树图,点击节点,出现弹窗,可以对节点进行增删和增加一条线,同事改变了数据,后台需要的数据直接传sharingMsg,这个数据是图当前的状态。效果图和代码如下

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>实例</title>
    <link type="text/css" rel="stylesheet" href="assets/css/joint.css" />
    <script type="text/javascript" src="jquery-1.12.4.js" ></script>
    <script type="text/javascript" src="lodash.js" ></script>
    <script type="text/javascript" src="assets/js/backbone-min.js" ></script>
    <script type="text/javascript" src="assets/js/joint.js" ></script>
    <style>
        .treeOperate{
            position: absolute;
            width: 100px;
            height: 40px;
            padding: 20px;
            background: url("login_bg.jpg") no-repeat center/100% 100% content-box;
        }
        .treeOperate a{
            position: absolute;
        }
        .treeOperate .addEle{
            bottom: 0;
            left: 0;
        }
        .treeOperate .addLink{
            bottom: 0;
            right: 0;
        }
        .treeOperate .deleteEle{
            top: 0;
            left: 0;
        }
    </style>
</head>
<body>
<div id="paper" class="paper"></div>
</body>
<script>

    //删除一个节点,要知道到父亲节点,删除父亲节点中相应childrenMsg中的元素以及linkMsg中相应元素。
    //然后在用找到这个节点以及他的所有子节点,删除

    //新增一个节点,要给sharingMsg中相应元素增加id,childrenMsg,linkMsg等信息
    //还要insideMsg中增加相应内容

    let graph = new joint.dia.Graph;

    let paper = new joint.dia.Paper({
        el: document.getElementById('paper'),
        model: graph,
        width: 600,
        gridSize: 10,
        drawGrid: true,
        background: {
            color: 'rgba(0, 255, 0, 0.3)'
        }
    });

    //生成节点,obj = {left,top,text}
    function createEle(obj) {
        let rect = new joint.shapes.standard.Rectangle();
        rect.position(obj.left, obj.top); //位置
        rect.resize(100, 40);   //大小
        rect.attr({
            body: {
                fill: 'blue',
                rx:20,  //rx,ry设置圆角
                ry:20,
            },
            label: {
                text: obj.text,
                fill: 'white'
            }
        });
        return rect;
    }

    //生成线 根据位置
    // custom link definition
    let CustomLink = joint.dia.Link.define('examples.CustomLink', {
        defaultLabel: {
            // attrs: { text: { text: '*' } }
        },
        smooth: true
    });
    //obj = {x1,y1,x2,y2}  obj ={source,x2,y2}
    function createLink(obj) {
        let link = new CustomLink({
            // source: { x: obj.x1, y: obj.y1 },
            source: { id: obj.source.id },
            target: { x: obj.x2, y: obj.y2 },
            // router: { name: 'manhattan' },
        });
        return link;
    }

    //根据起始元素生成的线
    function createLinkEle(source, target){
        let link = new CustomLink({
            source: { id: source.id },
            target: { id: target.id },
            // router: { name: 'manhattan' },
        });
        return link;
    }

    // graph.addCell();

    //定义一个装删除元素id的数组,每当删除元素时,存入id,每当生成元素时,先取这里面的值作为id
    //为空时取sharingMsg的length作为id
    let hadDelEles = [];

    //这个信息是接收来自后台的数据,其中的cid,parentID都是会变的,不是固定的,只起到判断父子节点的作用
    let sharingMsg = [
        {
            id:1,
            text:'第一规则',
            sign:1000,  //sign判断元素所在行,越上面的越大,确定元素所在y轴的位置
            layer:1,    //layer表示元素所在层级,确定元素所在x轴的位置
            parentID:null,
            childrenMsg:[2,3,4],
            //连接信息,判断
            linkMsg:['通过','拒绝','人工复核']
        },
        {
            id:2,
            parentID:1,
            text:'第2规则',
            sign:999,
            layer:2,
            childrenMsg:[5,6],
            linkMsg:['通过','拒绝']
        },
        {
            id:3,
            parentID:1,
            text:'第3规则',
            sign:1000,
            layer:2,
            childrenMsg:[],
            linkMsg:[]
        },
        {
            id:4,
            parentID:1,
            text:'第4规则',
            sign:1001,
            layer:2,
            childrenMsg:[],
            linkMsg:[]
        },
        {
            id:5,
            parentID:2,
            text:'第5规则',
            sign:998,
            layer:3,
            childrenMsg:[],
            linkMsg:[]
        },
        {
            id:6,
            parentID:2,
            text:'第6规则',
            sign:999,
            layer:3,
            childrenMsg:[],
            linkMsg:[]
        }
    ];
    //定义一个存放被删除元素id的数组,当新建时,从这里找,如果这个数组有,就从这个数组中取,如果没有,就从
    //设为sharingMsg的length

    //这个数组存放界面中存在的节点
    let insideMsg = [];

    //调整画布大小函数,返回一个对象,包含树的最大最小层,包含输的最大最小sign
    function adjustPaperSize() {
        //根据下面的值,判断画布大小,元素位置
        let maxSign = 0,
            minSign = sharingMsg[0].sign,
            maxLayer = 0,
            minLayer = sharingMsg[0].layer;
        for(let i=0;i<sharingMsg.length;i++){
            if(sharingMsg[i].layer<minLayer){
                minLayer = sharingMsg[i].layer;
            }
            if(sharingMsg[i].layer>maxLayer){
                maxLayer = sharingMsg[i].layer;
            }
            if(sharingMsg[i].sign<minSign){
                minSign = sharingMsg[i].sign;
            }
            if(sharingMsg[i].sign>maxSign){
                maxSign = sharingMsg[i].sign;
            }
        }
        paper.$el.css({
            width:(maxSign - minSign + 1) * 150 +50,
            height:(maxLayer - minLayer + 1) * 100 + 40
        });
        return {
            maxSign:maxSign,
            minSign:minSign,
            maxLayer:maxLayer,
            minLayer:minLayer
        }
    }

    //打开界面时渲染生成树图
    function newTreeChart() {
        //调整画布大小
        let tempSize = adjustPaperSize();
        let  minSign = tempSize.minSign,
            minLayer = tempSize.minLayer;
        //生成元素
        for(let i=0;i<sharingMsg.length;i++){
            //一个元素长100,宽40,水平间隔50,第一个距离左侧50,垂直距离20,最上一个距离上边30;
            let left = (sharingMsg[i].sign - minSign) * 150 + 50;
            let top = (sharingMsg[i].layer - minLayer) * 100 + 30;
            let obj = {
                left:left,
                top:top,
                text:sharingMsg[i].text
            };
            let ele = createEle(obj);
            insideMsg.push({
                self:ele,
            });
            //下面这句该变sharingMsg相应节点的cid,为了之后的操作
            sharingMsg[i].cid = ele.cid;
            graph.addCell(ele)
        }

        //生成线
        //遍历每个节点,判断是否要生成线
        sharingMsg.forEach(function (item) {
            //如果节点存在孩子,进一步操作
            if(item.childrenMsg.length>0){
                //存在子元素,需要连线,先找到来源元素source
                let source = null;
                //insideMsg里面才存有真正的节点
                for(let i=0;i<insideMsg.length;i++){
                    if(item.cid == insideMsg[i].self.cid){
                        source = insideMsg[i].self;
                        break;
                    }
                }
                console.log(source);

                let nowItem = item;
                //利用当前节点的childrenMsg中的子元素id从sharingMsg中找到相应点
                item.childrenMsg.forEach(function (item,index) {
                    console.log(3);
                    //设置目标元素
                    let target = null;
                    let tempTarget = null;
                    //设置线条颜色
                    let color = nowItem.linkMsg[index];
                    for(let i=0;i<sharingMsg.length;i++){
                        if(item == sharingMsg[i].id){
                            tempTarget = sharingMsg[i];
                            //获取真正的target
                            for(let j=0;j<insideMsg.length;j++){
                                if(tempTarget.cid == insideMsg[j].self.cid){
                                    target = insideMsg[i].self;
                                    let link = createLinkEle(source,target);
                                    if(color == '通过'){
                                        link.attr({
                                            '.connection': { stroke: 'green'},
                                        });
                                    }
                                    if(color == '拒绝'){
                                        link.attr({
                                            '.connection': { stroke: 'red',},
                                        });
                                    }
                                    if(color == '人工复核'){
                                        link.attr({
                                            '.connection': { stroke: 'blue',},
                                        });
                                    }
                                    graph.addCell(link);
                                    break;
                                }
                            }
                        }
                    }
                })
            }
        });
    }
    newTreeChart();

    //元素点击事件
    paper.on('cell:pointerclick',function (e,d) {
        console.log(e);
        console.log(d);
        let target = null;
        for(let i=0;i<insideMsg.length;i++){
            if(e.model.cid == insideMsg[i].self.cid){
                target = insideMsg[i].self;
            }
        }
        newTreeOperate(target);
        d.stopPropagation();
    });

    //生成
    function newTreeOperate(obj) {
        let html = '<div class="treeOperate">\n' +
            '    <a href="javascript:;" class="deleteEle">del</a>\n' +
            '    <a href="javascript:;" class="addEle">ele</a>\n' +
            '    <a href="javascript:;" class="addLink">link</a>\n' +
            '</div>';
        $('body').append(html);
        $('.treeOperate').css({
            //因为操作弹窗的直接父元素是body,$("body").append()所以位置要微调。
            //因为操作弹窗的直接父元素是body,$("body").append()所以位置要微调。
            left:obj.attributes.position.x - 12,
            top:obj.attributes.position.y - 12
        });
        $('body').on('click',function () {
            $('.treeOperate').remove();
        });
        $('.addEle').on('click',function () {
            let tempObj = {
                left:obj.attributes.position.x + 150,
                top:obj.attributes.position.y,
                text:'请选择规则',
            };
            let cell = createEle(tempObj);
            cell.attr('text/fill','black');
            cell.attr('rect/fill','#fff');
            //新增节点后要给sharingMsg中这个节点的父亲的childrenMsg中加入此节点id
            //要在sharingMsg中加入此节点相应信息,在insideMsg中加入此节点相应信息
            insideMsg.push({
                self:cell
            });
            let tempId = 0;
            if(hadDelEles.length>0){
                tempId = hadDelEles.pop();
            }else{
                tempId = sharingMsg.length;
            }
            let tempParentID = 0,
                tempSign = 0,
                tempLayer = 0;
            //找到此节点的父亲节点,为父亲节点加上孩子节点(该节点)的信息(id)
            sharingMsg.forEach(function (item) {
                if(item.cid == obj.cid){
                    tempParentID = item.id;
                    item.childrenMsg.push(tempId);
                    tempSign = item.sign + 1;
                    tempLayer = item.layer;
                }
            });
            sharingMsg.push({
                id:tempId,
                cid:cell.cid,   //绑定和真正元素的联系
                parentID:tempParentID, //这个id也要改
                text:'请选择规则',
                sign:tempSign,
                layer:tempLayer,
                childrenMsg:[],
                linkMsg:[]
            });
            graph.addCell(cell);
            let link = createLinkEle(obj, cell);
            graph.addCell(link);
            adjustPaperSize();
        });

        $('.addLink').on('click',function () {
            let tempObj = {
                source:obj,
                x2:obj.attributes.position.x + 150,
                y2:obj.attributes.position.y + 10
            };
            let link = createLink(tempObj);
            graph.addCell(link);
        });

        $('.deleteEle').on('click',function () {
            //删除元素时,在sharingMsg中先删除父亲节点中childrenMsg中相应id,在删除该节点以及他的子节点,删除的节点先存入一个数组nowDel
            //根据nowDel数组中的节点,在insideMdg这个真正存在节点的数组中删除相应节点。
            //这个a为需要删除的节点,
            let a = null;
            console.log(sharingMsg);
            for(let i=0;i<sharingMsg.length;i++){
                if(obj.cid == sharingMsg[i].cid){   //真正的节点和sharingMsg是靠cid来绑定相关的
                    a = sharingMsg[i]
                }
            }
            console.log(1111);
            //在父元素中删除该子节点的id
            sharingMsg.forEach(function(item){
                if(item.id == a.parentID){
                    let tempIndex = item.childrenMsg.indexOf(a.id);
                    item.childrenMsg.splice(tempIndex,1);
                }
            });

            let nowDel = [a];
            function fn(f) {
                //这里应该是可以优化的,push到nowDel的可以删除掉
                f.childrenMsg.forEach(function(item,index,array){
                    let ThisItem = item;
                    sharingMsg.forEach(function (item, index, array) {
                        if(ThisItem == item.id){
                            nowDel.push(item);
                            fn(item);
                        }
                    })
                });
            }
            fn(a);
            nowDel.forEach(function(item,index){
                for(let i=insideMsg.length-1;i>=0;i--){
                    if(item.cid == insideMsg[i].self.cid){
                        console.log(22);
                        insideMsg[i].self.remove();
                        insideMsg.splice(i,1);
                    }
                }
                for(let i=sharingMsg.length-1;i>=0;i--){
                    if(item.id == sharingMsg.id){
                        sharingMsg.splice(i,1);
                    }
                }
            })

        });
    }
</script>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值