动态生成决策树
自己写之前,也上网搜过,不过没找到,所以现在将自己的代码发出来,希望可以帮到朋友们。
功能如下:
这里的弹出框调用的是bootsrap的组件,所以代码仅供参考,复制上去功能是出不来的,js具体代码如下:
/** * Created by heshuaishuai on 2017/3/6. */ define( [ "PDUtilDir/grid", "PDUtilDir/util", "PDUtilDir/tool", "PDUtilDir/dialog", "PDUtilDir/searchBlock", "CommonUtilDir/dict", "CommonUtilCkEditorDir/ckeditor","Date", "DateCN", "css!DateCss" ], function(Grid, Util, Tool, Dialog, SearchBlock ,Dict) { //定义html全局变量 var html = ''; //定义一个具有初始值的json var treeJson = { ruleId:"", name:"规则", expression:"", id:"root" }; /** * 创建根节点 */ function buildRoot(){ html += '<div class="root" id="root">'; html += '<span class="name btn btn-default">规则</span>'; html += '<ul></ul>'; html += '</div> '; return html; } /** * 创建左右分支 */ function bulidBranch(node) { branchStyle(); html += '<div class="left" id='+node.yes.id+'>'; html += '<span class="name btn btn-default"></span>'; html += '<ul></ul>'; html += '</div>'; html += '<div class="right" id='+node.no.id+'>'; html += '<span class="name btn btn-default"></span>'; html += '<ul></ul>'; html += '</div>'; return html; } /** * 分支样式 */ function branchStyle(){ html += '<div>'; html += '<span class="vertical_left">成立</span>'; html += '<span class="vertical_right">不成立</span>'; html += '<span class="tran_left"></span>'; html += '<span class="tran_right"></span>'; html += '</div>'; return html; } /** * 计算分支样式长度 */ function branchWidth(id){ var array = id.split('-'); var i = array.length-1; var w = $('#'+id+'>span.name').outerWidth(); $('#'+id).width(w); if($('#'+id).attr('class') === 'left'){ var parents_w = $('#'+id).parent('ul').parent('div').outerWidth(); $('#'+id).css({'left':-(250-parents_w/2+w/2)+(i-1)*50+'px'}); } if($('#'+id).attr('class') === 'right'){ var parents_w = $('#'+id).parent('ul').parent('div').outerWidth(); $('#'+id).css({'right':-(250-parents_w/2+w/2)+(i-1)*50+'px'}); } $('#'+id+'>ul>div>span.tran_left').css({ 'width':250-i*50+'px', 'left':-(250-w/2)+i*50+'px' }); $('#'+id+'>ul>div>span.tran_right').css({ 'width':250-i*50+'px', 'right':-(250-w/2)+i*50+'px' }); $('#'+id+'>ul>div>span.vertical_left').css({ 'width':250-i*50+'px', 'left':-(250-w/2)+i*50+'px' }); $('#'+id+'>ul>div>span.vertical_right').css({ 'width':250-i*50+'px', 'right':-(250-w/2)+i*50+'px' }); $('#'+id+'>ul>div.left').css({'left':-(250-w/2+50)+i*50+'px'}); $('#'+id+'>ul>div.right').css({'right':-(250-w/2+50)+i*50+'px'}); } /** * 动态生成json */ function createJson( id , nodeObj , name , expression ){ var node = findNode(id , nodeObj ); node.expression = expression; node.name = name; node['yes'] = { name:"", id:node.id+'-true', expression:"" }, node['no'] = { name:"", id:node.id+'-false', expression:"" } } /** * 核心算法:深度先序遍历获取当前node */ function findNode( id , node ){ //递归 if( node ){ if( node.id == id ) return node; if( node.yes ){ var tempNode = findNode( id , node.yes); if( tempNode && tempNode.id == id ) return tempNode; } if( node.no){ var tempNode = findNode( id , node.no); if( tempNode && tempNode.id == id ) return tempNode; } } return false; } /** * 获取高亮事件 */ function bindFocusEvent( id ){ $('#' + id + '>span' ).unbind('click').bind( 'click' , function(){ resetButtonStyle(); $(this).addClass('btn-primary').removeClass('btn-default'); }); } /** * 点击添加条件表达式事件 */ function bindAddConditionButtonEvent(node){ $('#add').unbind('click').bind('click' , function(){ var currentNodeId = $('.btn-primary').parent('div').attr('id'); if($('#'+currentNodeId+'>ul').html() ===''){ popUpAddConditionDialog( currentNodeId,node ); } }); } /** * 调用添加表达式弹出框,返回json,动态渲染 */ function popUpAddConditionDialog( id,node ){ var currentNode = findNode( id , node ); var buttons = []; buttons.push( { name:"确定", //回调函数 callback:function(){ var name = $('#conditionName').val(); var expression = $('#conditionExpression').val(); if(name === '' && expression === ''){ notNullDialog('条件名称与条件表达式不能为空!'); }else if(name === ''){ notNullDialog('条件名称不能为空!'); }else if(expression === ''){ notNullDialog('条件表达式不能为空!'); }else{ $('#' + id + ' >span').text(name); createJson( id , currentNode , name , expression); dialog.hide(); html = ''; $('#' + id + '>ul').empty(); $('#' + id + '>ul').append(bulidBranch(currentNode)); var childYesId = $('#' + id + ' >ul>.left').attr('id'); var childNoId = $('#' + id + ' >ul>.right').attr('id'); bindFocusEvent( childYesId ); bindFocusEvent( childNoId ); branchWidth(id); } } } ); var dialog = Dialog({ id:"BaseDialog", cache:false, title:"条件内容", width:"400px", hieght:"200px", dialogsize:"modal-sm", body:"", buttons:buttons }); var dialogHtml = ''; dialogHtml += '<form class="form-horizontal">'; dialogHtml += '<div class="form-group">'; dialogHtml += '<label class="col-sm-3">条 件 名 称:</label>'; dialogHtml += '<input id="conditionName" class="col-sm-8" type="text"'; dialogHtml += 'placeholder="Conditional name" maxlength="20"/>'; dialogHtml += '</div>'; dialogHtml += '<div class="form-group">'; dialogHtml += '<label class="col-sm-3">条件表达式:</label>'; dialogHtml += '<textarea id="conditionExpression" class="col-sm-8"'; dialogHtml += 'placeholder="Conditional expression" maxlength="200"'; dialogHtml += 'rows="3"></textarea>'; dialogHtml += '</div>'; dialogHtml += '</form>'; dialog.setBody(dialogHtml); dialog.show(); } /** * 点击设置结果事件 */ function bindSetResultButtonEvent(node){ $('#result').unbind('click').bind('click' , function(){ var currentNodeId = $('.btn-primary').parent('div').attr('id'); if($('#'+currentNodeId+'>ul').html() ===''){ popUpSetResultDialog( currentNodeId ,node); } }); } /** * 调用设置结果弹出框 */ function popUpSetResultDialog( id ,node){ var currentNode = findNode( id , node ); var buttons = []; buttons.push( { name:"确定", //回调函数 callback:function(){ var name = $('#resultName').val(); var expression = $('#resultExpression').val(); if(name === '' && expression === ''){ notNullDialog('结果名称与结果表达式不能为空!'); }else if(name === ''){ notNullDialog('结果名称不能为空!'); }else if(expression === ''){ notNullDialog('结果表达式不能为空!'); }else{ $('#' + id + ' >span').text(name); currentNode.name = name; currentNode.expression = expression; branchWidth(id); dialog.hide(); } } } ); var dialog = Dialog({ id:"BaseDialog", cache:false, title:"设置结果", width:"400px", hieght:"200px", dialogsize:"modal-sm", body:"", buttons:buttons }); var dialogHtml = ''; dialogHtml += '<form class="form-horizontal">'; dialogHtml += '<div class="form-group">'; dialogHtml += '<label class="col-sm-3">结 果 名 称:</label>'; dialogHtml += '<input id="resultName" class="col-sm-8" type="text"'; dialogHtml += 'placeholder="Result name" maxlength="20"/>'; dialogHtml += '</div>'; dialogHtml += '<div class="form-group">'; dialogHtml += '<label class="col-sm-3">结果表达式:</label>'; dialogHtml += '<textarea id="resultExpression" class="col-sm-8"'; dialogHtml += 'placeholder="Result expression" maxlength="200"'; dialogHtml += 'rows="3"></textarea>'; dialogHtml += '</div>'; dialogHtml += '</form>'; dialog.setBody(dialogHtml); dialog.show(); } /** * 点击删除事件 */ function bindDeleteButtonEvent(node){ $('#delete').unbind('click').bind('click' , function(){ var currentNodeId = $('.btn-primary').parent('div').attr('id'); var ulHtml = $('#'+currentNodeId+'>ul').html(); if(ulHtml !=='' && ulHtml !==undefined){ popUpDeleteDialog( currentNodeId ,node); } }); } /** * 调用删除弹出框 */ function popUpDeleteDialog( id ,node){ var currentNode = findNode( id , node ); var buttons = []; buttons.push( { name:"确定", //回调函数 callback:function(){ $('#'+id+' ul').empty(); $('#' + id + ' >span').text(''); currentNode.name = ''; currentNode.expression = ''; delete currentNode.yes; delete currentNode.no; dialog.hide(); } } ); var dialog = Dialog({ id:"BaseDialog", cache:false, title:"添加内容", width:"400px", hieght:"200px", dialogsize:"modal-sm", body:"确定删除吗?", buttons:buttons }); dialog.show(); } /** * 调用不能为空弹出框 */ function notNullDialog( text ){ var buttons = []; buttons.push( { name:"确定", //回调函数 callback:function(){ dialog.hide(); } } ); var dialog = Dialog({ id:"BaseDialog", cache:false, title:"提示", width:"300px", hieght:"100px", dialogsize:"modal-sm", body:text, buttons:buttons }); dialog.show(); } /** * 重置样式 */ function resetButtonStyle(){ $('#decisionTree .btn-primary').addClass('btn-default').removeClass('btn-primary'); } /** * 根据json,深度遍历生成DOM结构 */ function generateDecisionTree(node){ if(node){ var suffix = branchPosition(node.id); if(suffix === 'root'){ html += '<div class="root" id='+node.id+'><span class="name btn btn-default">'+node.name+'</span><ul>'; } if(node.yes && node.no){ if(suffix === 'true'){ html += '<div class="left" id='+node.id+'><span class="name btn btn-default">'+node.name+'</span><ul>'; }else if(suffix === 'false'){ html += '<div class="right" id='+node.id+'><span class="name btn btn-default">'+node.name+'</span><ul>'; } branchStyle(); }else{ if(suffix === 'true'){ html += '<div class="left" id='+node.id+'><span class="name btn btn-default">'+node.name+'</span><ul>'; }else if(suffix === 'false'){ html += '<div class="right" id='+node.id+'><span class="name btn btn-default">'+node.name+'</span><ul>'; } } if(node.yes){ generateDecisionTree(node.yes); } if(node.no){ generateDecisionTree(node.no); } html +='</ul></div>'; } return html; } /** * 根据id后缀,判断左右分支 */ function branchPosition(id){ var array = id.split('-'); var length = array.length; var suffix = array[length-1]; return suffix; } /** * 深度遍历:渲染后获取焦点 */ function getRenderFocus(node){ bindFocusEvent( node.id ); branchWidth(node.id); if(node.yes){ getRenderFocus(node.yes); } if(node.no){ getRenderFocus(node.no); } } /** * 点击生成,渲染页面 */ function bindGenerateButtonEvent(){ $('#generate').unbind('click').bind('click',function(){ html = ''; $('#decisionTree').empty(); $('#decisionTree').html(generateDecisionTree(treeJson)); getRenderFocus(treeJson); }); } /** * 点击保存,弹出json字符串 */ function bindSaveButtonEvent(node){ $('#save').bind('click',function(){ var treeString = JSON.stringify(node); alert(treeString); }); } /** * 初始化数据 */ function initData(){ $.ajax({ type:'GET', url:getServer()+'/static/app/collection/decisionTree/treeJson.json', dataType:'JSON', success:function(data){ if(data){ if(data.ruleId === ''){ $('#decisionTree').html(buildRoot()); bindFocusEvent( 'root' ); init(treeJson); }else{ html = ''; $('#decisionTree').empty(); $('#decisionTree').html(generateDecisionTree(data)); getRenderFocus(data); init(data); } } } }); } /** * 初始化 */ function init(node){ bindAddConditionButtonEvent(node); bindSetResultButtonEvent(node); bindDeleteButtonEvent(node); bindSaveButtonEvent(node); } /** * 主函数 */ return { mainInit : initData } });
代码里三次用到了二叉树的先序遍历算法,核心算法也是二叉树的递归遍历算法,二叉树的递归遍历方法前面有说过。