1、想要实现一个自由组合查询条件的功能,给不会写SQL语句的人使用。比如生成的查询条件树如下图:
它表达的查询条件是:( 1=1 AND ATTACK_TIMES > 8 OR ( ATTACK_SOURCE = 外网 AND ATTACK_TYPE = SHELL脚本 ) )
2、分析这个需求,核心是父节点和其子节点要作为一个整体条件:
(1)根节点的默认条件是1=1,如果没有子节点,查询条件就是1=1;
(2)在根节点下添加一个节点:AND 攻击次数 大于8,查询条件变为:1=1 and attack_times>8;(节点的数据包括哪些属性,后边再说)
(3)在根节点下再增加一个节点:OR 攻击来源 等于外网,查询条件变为:1=1 and attck_times>8 or attack_src='外网';
(4)在OR 攻击来源 等于外网这个节点下添加一个节点:AND攻击类型 等于SHELL脚本,则OR这节点的整体条件为:or( attack_src='外网' and attack_type='SHELL脚本'),整棵树表示的条件是:( 1=1 AND ATTACK_TIMES > 8 OR ( ATTACK_SOURCE = 外网 AND ATTACK_TYPE = SHELL脚本 ) )
3、因为节点可以无限添加,则可以据此构造任何查询条件。下面说一下节点数据的结构,
(1)id:'0101' id的主要功能,是记住当前选择的节点,后续的添加、修改或删除操作,都要用到这个值;
(2)name:'AND 攻击次数 大于8' name主要用于显示节点,就是我要看到的节点名字;
(3)cron:' and attack_times >8' cron表示的是当前节点的查询条件,如果name属性的值和cron相同,则可以不要这个属性;
(4)children:[{}] children用来表示当前节点的子节点。
4、整棵树的数据结构是一个无限嵌套的json数组。初始的数据结构为:
var nodeData = [
{
name: '根节点'
,id: '01'
,cron: ' 1=1 '
,children: []
}
];
初始化树的方法是:
layui.use(['form','tree', 'layer'], function(){
var layer = layui.layer
,$ = layui.jquery;
var form = layui.form;
var tree = layui.tree;
layui.tree({
elem: '#demo1' //指定元素
,click: function(obj){
nodeSelect(obj);
}
,showLine: true
,target: '_blank'
,nodes: nodeData
});
});
nodeSelect函数的功能是记住当前节点的id值,并设置节点的选中效果,具体代码如下:
function nodeSelect(obj){
selectedNodeId = obj.id;
var citeArr = $("cite");
$(".tree-txt-active").removeClass("tree-txt-active");
for(var i=0;i<citeArr.length;i++){
if($(citeArr[i]).html() == obj.name){
$(citeArr[i]).attr('class','tree-txt-active');
return false;
}
}
}
selectedNodeId是全局变量,tree-txt-active是选中节点的样式,自己随意定义,我这里只定义了一个颜色,用于和没有选中的节点进行区分。
这里有一个问题,就是layui的树节点点击事件,拿到的是该节点的数据,不是节点对象本身。上边的函数是根据选中节点的name值和树节点中cite元素的文本值进行对比,相同就认为是选中的节点。如果有两个节点的名称一样,则不能保证设为选中样式的节点,就是真实选中的节点(选中节点的数据,是真实选中的数据)。严谨的做法是,根据选中节点的数据在nodeData的层次位置,来找到树对应的层次位置的名称相同的节点。
5、树的增删改操作
操作按钮如下图:
(1)新增:
var isNew = false;
function add(){
if(selectedNodeId == ''){
layer.msg('请选择一个节点');
return false;
}
isNew = true;
$('#cond1').show();
$('#logicOp').val("AND");
$('#field').val("");
$('#opType').val("");
$('#val').val("");
}
cond1是一个用于编辑节点属性的div,大概张这样:
第一个下拉框是选中and或or逻辑关系,第二个是实体属性的显示名称和数据库列名,第三个是逻辑符号,如大于、小于、等于、介于、包含(于)等,最后一个是条件的值。点击“确定”按钮,则把新节点的数据更新到全局变量nodeData中,然后重新渲染树。新增函数代码如下:
function saveNode(){
//取值
var logicOp = $('#logicOp').val();
var fieldName = $("#field").find("option:selected").text();
var fieldVal = $('#field').val();
var opName = $("#opType").find("option:selected").text();
var opVal = $('#opType').val();
var val = $('#val').val();
//处理包含于、包含和介于运算符
if(opVal == 'in' || opVal == 'like' || opVal == 'between'){
val = getSeveralVal(opVal,val);
}
// layer.msg(fieldName+',' +fieldVal+',' +opName+',' +opVal+',' +val + ',selectedNodeId:' + selectedNodeId);
//根据节点id找到父节点数据
parentNode = getNode(selectedNodeId,nodeData);
//找到父节点下最大的子节点id值
maxChildId = getMaxChildId(selectedNodeId,parentNode);
//设置新节点的id(每层两位,值递增)
childNodeId = '';
if(maxChildId == ''){
childNodeId = selectedNodeId + '01';
}else{
var tmpId = Number(maxChildId) +1;
childNodeId = selectedNodeId +(tmpId>9?tmpId:('0'+ tmpId));
}
//将新节点数据添加到全局变量nodeData中
addNode(selectedNodeId,nodeData,{id:childNodeId,name:logicOp + fieldName + " " + opName + val,children:[],cron: '' + logicOp + ' ' + fieldVal + ' ' + opVal + ' ' + val});
//根据nodeData生成查询表达式
geneCron();
//隐藏编辑div
$('#cond1').hide();
//清空树
$("#demo1").find("li").remove();
//重新渲染树
layui.tree({
elem: '#demo1' //指定元素
,click: function(obj){
nodeSelect(obj);
}
,showLine: true
,target: '_blank'
,nodes: nodeData
});
}
(2)修改
点击修改按钮,先获取选中节点的数据,然后将数据填充到条件编辑页面,修改保存后,将新的查询条件数据更新到nodeData,再重新渲染树。
(3)删除
点击删除按钮,根据选中节点,遍历nodeData,找到对应的节点后,通过splice函数删除该节点,然后重新渲染树。
来源:oschina
链接:https://my.oschina.net/u/161393/blog/4345014