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函數刪除該節點,然後重新渲染樹。