寫在片頭:乾的是運維工作,愛好動手。純屬個人項目,身兼業務需求人員,產品經理,前端,後端,測試於一體,代碼層面會有邏輯問題,請各位看官見諒,下文只是記錄我一個10手碼農在前端踩過的坑和一些思路。
###應用樹: 爲了方便管理以業務爲單位的服務集羣,利用樹形結構建立起了一種服務組織關係,方便運維日常管理。先上一張效果圖:
#一、 Jstree
看過XX公司的運維平臺的應用樹後,從此不能自拔,勵志也要寫出一樣效果的東東。採用的jsTree
,是基於javascript的一個跨瀏覽器樹控件樹行,功能強大,最重要的是免費,
左側的樹列表的生成因爲會用到mysql的group by,所以採用了redis存儲,需要的時候才更新服務樹,減少對數據庫的壓力,項目用到了根據選擇樹節點點擊事件功能。
既點擊項目名稱的紅×××標時,會加載整個項目涉及到的服務器,效果圖:
點擊項目下單個主機時,會加載這個服務器的應用情況,效果圖:
代碼如下:
("changed.jstree", function (e, data) {
var items = data.node.text;
var icon = data.node.icon;
if (icon == "fa fa-building-o font-blue"){
document.getElementById("group1").style.display="none";
document.getElementById("group2").style.display="block";
$.ajax({
type: "GET",
url: "../cmdb_app_info_ajax/?ip="+items,
dataType:'json',
async: false,
beforeSend:function(){
Metronic.blockUI({animate: true});
},
complete: function() {
Metronic.unblockUI();
},
success: function(data){
var sOut= '';
sOut += '<tr>'
sOut += '<td>' + data['設備PHP版本號:'] + '</td>';
sOut += '<td>' + data['設備PHP路徑:'] + '</td>';
sOut += '<td>' + data['設備PHP端口號:'] + '</td>';
sOut += '<tr>'
sOut += '<th><b>TOMCAT版本號</b></th>';
sOut += '<th><b>TOMCAT路徑</b></th>';
sOut += '<th><b>TOMCAT端口號</b></th>';
sOut += '</tr>'
sOut += '<td>' + data['設備TOMCAT版本號:'] + '</td>';
sOut += '<td>' + data['設備TOMCAT路徑:'] + '</td>';
sOut += '<td>' + data['設備TOMCAT端口號:'] + '</td>';
sOut += '<tr>'
sOut += '<th><b>NGINX版本號</b></th>';
sOut += '<th><b>NGINX路徑</b></th>';
sOut += '<th><b>NGINX端口號</b></th>';
sOut += '</tr>'
sOut += '<td>' + data['設備NGINX版本號:'] + '</td>';
sOut += '<td>' + data['設備NGINX路徑:'] + '</td>';
sOut += '<td>' + data['設備NGINX端口號:'] + '</td>';
sOut += '<tr>'
sOut += '<th><b>MYSQL版本號</b></th>';
sOut += '<th><b>MYSQL路徑</b></th>';
sOut += '<th><b>MYSQL端口號</b></th>';
sOut += '<th><b>MYSQL數據庫</b></th>';
sOut += '</tr>'
sOut += '<td>' + data['設備MYSQL版本號:'] + '</td>';
sOut += '<td>' + data['設備MYSQL路徑:'] + '</td>';
sOut += '<td>' + data['設備MYSQL端口號:'] + '</td>';
sOut += '<td>' + data['設備MYSQL數據庫:'] + '</td>';
sOut += '<tr>'
sOut += '<th><b>定時任務</b></th>';
sOut += '</tr>'
sOut += '<td>' + data['設備定時任務:'] + '</td>';
sOut += '</tr>'
$("#cmdb_app_info_get").html(sOut);
},
error: function (data) {
toastr.error('沒有數據可以加載')
}
});
}else if (icon == "fa fa-folder icon-state-warning icon-lg"){
document.getElementById("group1").style.display="block";
document.getElementById("group2").style.display="none";
$.ajax({
type: "GET",
url: "../cmdb_subtitle_info_ajax/?subtitle="+items,
dataType:'json',
async: false,
beforeSend:function(){
Metronic.blockUI({animate: true});
},
complete: function() {
Metronic.unblockUI();
},
success: function(data){
if ($('#product_tree').hasClass('dataTable')) {
console.log('重新加載datatable,準備初始化................')
$('#product_tree').dataTable().fnClearTable(false) //清空一下table
$('#product_tree').dataTable().fnDestroy();//還原初始化datatable
}
$("#cmdb_subtitle_info_get").html("");
$.each(data,function() {
var sOut= '';
sOut += '<tr>'
sOut += '<td><input type="checkbox" class="checkboxes" value="1"/></td>'
sOut += '<td id='+this['fields']['unit_title']+'>' + '<a href="../cmdb_assets_list/?place=&status=&year=&type=&model=&search='+ this['fields']['unit_ip'] +'">' + this['fields']['unit_title'] + '</a>' + '</td>';
sOut += '<td>' + this['fields']['unit_subtitle'] + '</td>';
sOut += '<td id='+this['fields']['unit_ip']+'>' + this['fields']['unit_ip'] + '</td>';
sOut += '<td>' + this['fields']['unit_use_type'] + '</td>';
sOut += '<td><a class="fa fa-eye" href="#responsives" data-toggle="modal"></a></td>';
sOut += '</tr>'
$("#cmdb_subtitle_info_get").append(sOut);
});
},
error: function (data) {
toastr.error('沒有數據可以加載')
}
});
}
$(document).ready(function(){
eyeClick();
TableManaged.init() //初始化datatables
})
}
撿一些重要的說:
var items = data.node.text; //獲取圖標名稱
var icon = data.node.icon; //獲取圖標
我是通過判斷圖標的樣式來獲取用戶點擊的是哪裏,沒辦法,誰叫咱是10手碼農。同時有group1和gourp2兩個tables,選擇哪個圖標就彈出哪個tables,隱藏另外的,在通過ajax後臺獲取數據,利用each循環獲得的結果,構建出一個tr的內容,最後在添加到tables裏,以此實現“山寨版”的數據加載。
####這裏踩過的坑:
因爲我要先加載完數據,然後纔是初始化datatables表格。當點擊到別的業務圖標時,又要經歷一次加載數據,然後初始化表格。結果datatables報錯了....cannot reinitialise datatable
,大概意思就是datatables不能重複初始化。
最後只能通過判斷加載後的tables是否被加載後,如果加載過,先銷燬,在初始化。
if ($('#product_tree').hasClass('dataTable')) {
console.log('重新加載datatable,準備初始化................')
$('#product_tree').dataTable().fnClearTable(false) //清空一下table
$('#product_tree').dataTable().fnDestroy();//還原初始化datatable
}
#二、 Datatable
右邊就是數據展示採用datatables,可以將任何HTML表格添加高級的交互功能,這裏用到了checkbox的單選和多選。
datatables代碼如下:
var TableManaged = function () {
var initTable2 = function () {
var table = $('#product_tree');
table.dataTable({
"bDestroy": true,
"language": {
"aria": {
"sortAscending": ": activate to sort column ascending",
"sortDescending": ": activate to sort column descending"
},
"emptyTable": "未有相關數據",
"info": "當前顯示 _START_ 到 _END_ 條,共 _TOTAL_ 條記錄。",
"infoEmpty": "當前顯示0到0條,共0條記錄",
"infoFiltered": "(數據庫中共爲 _MAX_ 條記錄)",
"lengthMenu": "顯示 _MENU_ 記錄",
"search": "模糊查詢:",
"zeroRecords": "對不起,查詢不到任何相關數據",
"oPaginate": {
"sFirst": "首頁",
"sPrevious": " 上一頁 ",
"sNext": " 下一頁 ",
"sLast": " 尾頁 "
}
},
"bStateSave": true, // save datatable state(pagination, sort, etc) in cookie.
"lengthMenu": [
[5, 15, 20, -1],
[5, 15, 20, "All"] // change per page values here
],
// set the initial value
"pageLength": 5,
"columnDefs": [{ // set default column settings
'orderable': false,
'targets': [0]
}, {
"searchable": false,
"targets": [0]
}],
"order": [
[1, "asc"]
] // set first column as a default sort by asc
});
var tableWrapper = jQuery('#product_tree_wrapper');
var host_list = [];
var release_build_job = '';
//全選
table.find('.group-checkable').change(function () {
var set = jQuery(this).attr("data-set");
var checked = jQuery(this).is(":checked");
host_list.length = 0;
jQuery(set).each(function () {
if (checked) {
$(this).attr("checked", true);
var hosts = $(this).parent().parent().find('td').eq(1).attr("id");
host_list.push(hosts);
document.getElementById("group3").style.display="block";
} else {
$(this).attr("checked", false);
host_list.length = 0;
document.getElementById("group3").style.display="none";
}
});
jQuery.uniform.update(set);
});
//單選
table.on('change', 'tbody tr .checkboxes', function () {
var hosts = $(this).parent().parent().find('td').eq(1).attr("id");
var checked = jQuery(this).is(":checked");
if (checked) {
$(this).attr("checked", true);
host_list.push(hosts);
if (host_list.length > 1) {
document.getElementById("group3").style.display="block";
}else {
document.getElementById("group3").style.display="none";
}
} else {
$(this).attr("checked", false);
host_list.pop(hosts);
if (host_list.length > 1) {
document.getElementById("group3").style.display="block";
}else {
document.getElementById("group3").style.display="none";
}
}
});
tableWrapper.find('.dataTables_length select').select2(); // initialize select2 dropdown
return {
//main function to initiate the module
init: function () {
if (!jQuery().dataTable) {
return;
}
initTable2();
}
};
}();
撿一些重要的說:
if (host_list.length > 1) {
document.getElementById("group3").style.display="block";
}else {
document.getElementById("group3").style.display="none";
}
判斷了checkbox選擇的數量,大於1個的話,彈出gourp3(一個批量繪圖按鈕),效果圖如下:
#三、 Multiselect
接上文,這裏平臺是結合了openfalcon
做的監控,點擊上文的監控指標項按鈕,會彈出一個multiselect
層,用過openfalcon
的都知道,監控指標多是他的特色之一,所以在選型的時候第一個就想到得需要一個帶filter
功能的select
控件,效果圖如下:
代碼如下:
$('#get_hostlist_counter_select').multiselect({
buttonWidth: '500px',//按鈕寬度
nonSelectedText: '---- 請選擇監控指標 ----',
nSelectedText: '個被選中',
enableCaseInsensitiveFiltering: true,//不區分大小寫
filterPlaceholder: '模糊查詢',
enableFiltering: true,
onDropdownShow: function(event) {
get_hostlist_counter()
},
onChange: function (option, checked, select) {
var selectedOptions = $('#get_hostlist_counter_select option:selected');
if (selectedOptions.length >= 6) {
// 禁用選項
toastr.error('監控指標最多同時選取6個')
var nonSelectedOptions = $('#get_hostlist_counter_select option').filter(function() {
return !$(this).is(':selected');
});
nonSelectedOptions.each(function() {
var input = $('input[value="' + $(this).val() + '"]');
input.prop('disabled', true);
input.parent('li').addClass('disabled');
});
}
else {
// 啓動選項
$('#get_hostlist_counter_select option').each(function() {
var input = $('input[value="' + $(this).val() + '"]');
input.prop('disabled', false);
input.parent('li').addClass('disabled');
});
}
}
});
撿重要的說:
用到了multiselect
的onDropdownShow
和onChange
兩個回調函數,onDropdownShow
可以理解成就是點擊菜單下拉事件,觸發了get_hostlist_counter()
通過ajax取數據填充options
,代碼如下:
function get_hostlist_counter(){
var options_list = []
var obj2 = new Object();
var jsonData ={
'unit_title': host_list.join(','),
};
$("#get_hostlist_counter_select").empty();
$.ajax({
async: false,
type: "POST",
url : "../openfalcon_get_endpoint_counter_ajax/",
data: jsonData,
cache: false,
dataType: "json",
beforeSend:function(){
Metronic.blockUI({animate: true});
},
complete: function() {
Metronic.unblockUI();
},
success: function(obj) {
if (obj['counter'] == ''){
toastr.error('獲取監控指標爲空或者異常')
}else{
for (var id in obj['counter']){
obj2 = {
label : obj['counter'][id],
value : obj['counter'][id],
};
options_list.push(obj2)
}
$('#get_hostlist_counter_select').multiselect('dataprovider', options_list);
}
}
});
}
onChange
可以理解成點擊事件,監控指標這塊還是怕傳多了,壓垮了openfalcon
的graph
的,所以限制最多傳6個,超過的話會禁用所有選項。
#四、 繪圖
最後就是一些繪圖了,結合着openfalcon
的API做的,下篇文章再議,效果圖: