今天重新整理以前封装的树结构控件,用弹性布局替代了宽度计算,支持实时响应宽度改变,并且添加了事件回调等配置项,同时修复了一些遗留的bug
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>test</title>
<script type="text/javascript" src="../js/jquery-3.1.1.min.js"></script>
<style type="text/css">
html,body{ margin:0;padding:0;width:100%;height:100%; }
</style>
</head>
<body>
<div id="testTree" style="width:200px;height:600px;margin:50px;border: 1px solid red"></div>
</body>
</html>
<script type="text/javascript">
if (typeof jQuery === 'undefined') { throw 'no jquery'; }
(function () {
window.UETree = function (container, data, config) {
//私有属性
var parent = $('<div></div>'),
defaultConfig = {
isOpen: false, //是否展开
hasCreate: false, //是否显示添加、删除按钮
hasCheckbox: false, //是否显示勾选框
flagType: 'addReduce', //采用何种图标样式,默认加减符号,file\triangle\addReduce
flags: { //图标样式,可提供实心三角形、加减符、文件与文件夹等选项
triangle: {
top: 4,
open: 'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAABPSURBVDhPY/wPBAwUACYoTTYYNYCBASUWEhISoCzcYMGCBVAWBKC4AF0SHWCTx/ACLkNwiWMNA3TFeF0GCgNcID4+HsrCDUaTMsUGMDAAAKIFWDAh58ynAAAAAElFTkSuQmCC',
close: 'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAABRSURBVDhPY/wPBAwUACYoTTYYxAYkJCRAWfgBXhcQYwhBLxAyhKgwwGcI0YGIyxCiDViwYAGUhQqIMgCXZhAgaAA+zSCA1wBCmkFgyGcmBgYAX8sVNxW3k9cAAAAASUVORK5CYII=',
leaf: ''
},
addReduce: { open: '+', close: '-', leaf: '' },
file: {
top: 4,
open: 'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAB7SURBVDhPY/wPBAwUACYoTTZAccGTI8VQFiqQsemFsjAB3ACQZmlzJ7AgOnh6ch9OQ4gyAARAhqADkKFEG4ANgAylPBAfHy4iORphLgW5AGwAOU4H6aGOF0CBSG4AggDFLgAbAIpPmInEApiLUdIBOQBuALmAwjBgYAAASyROWwOfit0AAAAASUVORK5CYII=',
close: 'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAABYSURBVDhPY/wPBAwUACYoTTZAccGTI8VQFiqQsemFsjAB3ACQZhlLN7AgOnhyfBdOQ4gyAARAhqADkKFEG4ANgAylOBBHDRgMBhCVlHEBlIRELqDQCwwMAHrpMxmKmvRfAAAAAElFTkSuQmCC',
leaf: 'data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAABfSURBVDhPY/wPBAwUACYoTTZAcUHHqrtQFiqoCFOGsrAAkAEw0L7yDpSFCnCJgwBBL4BcBXIBLtcR5QIYwCZPlAtgNDaXEDQAFoC4ApLiaBw1gMikjA6QY2SgcyMDAwC9IJ/Uo4UIgAAAAABJRU5ErkJggg=='
}
},
selectCallback: null, //选中行事件的回调方法
deleteItemCallback: null, //点击删除当前节点事件的回调方法
addChildCallback: null, //点击添加子节点事件的回调方法
lineHeight: 24, //行高
overLineBGColor: "#daecfd", //鼠标滑过行背景色
selectLineBGColor: "#ace", //选中行背景色
overLineTextColor: "#000", //鼠标滑过文字颜色
selectLineTextColor: "#000" //选中文字颜色
}, //默认配置
baseStyle = function (c) {
if ($('#ue_tree_style').length < 1) {
var f = c.flags[c.flagType];
$('head').append(
'<style id="ue_tree_style">'
+ ' .ue_tree_container {position:relative;padding:10px;}'
+ ' .ue_tree_frame {position:relative;width:100%;border:none;background-color:#fff;font-size:12px;margin:0;height:auto;clear:both;display:inline-block;}'
+ ' .ue_tree_item {position:relative;display:flex;}'
+ ' .ue_tree_flag {width:16px;height:' + c.lineHeight + 'px;line-height:' + c.lineHeight + 'px;font-size:smaller;display:inline-block;}'
+ ' .ue_tree_chk {display:inline-block;margin-top:7px !important;}'
+ ' .ue_tree_content {font-size:smaller;cursor:default;flex:1;padding:0px;display:inline-block;}'
+ ' .ue_tree_img {margin-top:' + f.top + 'px;}'
+ ' .ue_tree_text {float:left;height:' + c.lineHeight + 'px;line-height:' + c.lineHeight + 'px;cursor:pointer;flex:1;}'
+ ' .ue_tree_text:hover {background-color:' + c.overLineBGColor + ';}'
+ ' .ue_tree_btn {width:10px;height:10px;line-height:8px;border:1px solid #bdf;border-radius:3px;color:#39f;}'
+ ' .ue_tree_del {margin:7px 0 0 5px;float:left;font-size:18px;}'
+ ' .ue_tree_add {margin-top:7px;float:right; }'
+ ' .ue_tree_btn:hover {background-color:#bdf;}'
+ ' .ue_tree_treeItem_select{background:' + c.selectLineBGColor + '}'
+ '</style>');
}
},
formatData = function (g, d) {
if(d.id == 0){
for(var i = 0; i < g.data.length; i++){
var r = g.data[i];
var p = g.data.filter(function (item) {
return item.id == r.parentId;
});
if(!p || p.length < 1){
d.children.push(r);
}
}
}else{
d.children = g.data.filter(function (item) {
return item.parentId == d.id;
});
}
for(var i = 0; i < d.children.length; i++){
var r = d.children[i];
d.isLeaf = false;
r.parentId = d.id;
r.depth = d.depth + 1;
r.isLeaf = true;
r.path = d.id + '_' + r.id;
formatData(g, r);
};
return d;
},
changeState = function (g, d) {
var c = g.config,
f = c.flags[c.flagType],
i = d.attr('idx'),
h = d.parent().find('.ue_tree_frame[idx="' + i + '"]');
if (d.attr('state') == 'open') {
if (h.length > 0) {
h.hide();
}
var m = c.flagType == 'addReduce' ? f.close : '<img class="ue_tree_img" alt="" src="' + f.close + '"/>';
d.attr('state', 'close').html(m);
} else {
if(d.attr('state') == 'close') {
if (h.length > 0) {
h.show();
} else {
var tmp = i.split('_');
var id = tmp[tmp.length - 1];
var dt = g.getDataById(id);
addContent(g, d.parent().find('.ue_tree_content[idx="' + i + '"]'), dt);
}
var m = c.flagType == 'addReduce' ? f.open : '<img class="ue_tree_img" alt="" src="' + f.open + '"/>';
d.attr('state', 'open').html(m);
}
}
},
bindEvent = function (g) {
var c = g.config;
var d = $('.ue_tree_container');
d.on('click', '.ue_tree_text', function () {
$(".ue_tree_treeItem_select", parent).removeClass('ue_tree_treeItem_select');
$(this).addClass('ue_tree_treeItem_select');
g.selectedId = $(this).attr('idx');
g.selectedItem = g.getDataById(g.selectedId);
c.selectCallback && c.selectCallback(g.selectedId, g.selectedItem, $(this));
});
d.on('click','.ue_tree_flag', function () {
changeState(g, $(this));
});
d.on('click','.ue_tree_del', function () {
var id = $(this).attr('idx');
var item = g.getDataById(id);
if(c.deleteItemCallback && c.deleteItemCallback(id, item, $(this))){
item.status = 'deleted'
$('.ue_tree_item[idx="' + id + '"]').remove();
}
});
d.on('click','.ue_tree_add', function () {
var id = $(this).attr('idx');
var item = g.getDataById(id);
if(c.addChildCallback){
var dt = c.addChildCallback(id, item, $(this));
g.addChild(item, dt);
}
});
},
addContent = function (g, p, d) {
var c = g.config;
if (d.children && d.children.length > 0) {
var q = $('<div idx="' + d.id + '" class="ue_tree_frame"></div>').appendTo(p);
c.isOpen && q.show();
for (var i = 0; i < d.children.length; i++) {
var cw = addItem(q, d.children[i], c);
}
}
},
addItem = function (p, d, c) {
if(d.status == 'deleted'){
return false;
}
var f = c.flags[c.flagType],
t = c.isOpen && d.children && d.children.length > 0,
m = $('<div idx="' + d.id + '" class="ue_tree_item"></div>').appendTo(p),
flag = d.isLeaf ? f.leaf : t ? f.open : f.close,
html = c.flagType == 'addReduce' ? flag : flag == '' ? '' : '<img class="ue_tree_img" alt="" src="' + flag + '"/>';
$('<div idx="' + d.id + '" state="' + (d.isLeaf ? 'leaf' : t ? 'open' : 'close') + '" class="ue_tree_flag">' + html + '</div>').appendTo(m);
if (c.hasCheckbox) {
m.append('<input type="checkbox" class="ue_tree_chk" />');
}
var n = $('<div idx="' + d.id + '" class="ue_tree_content"></div>').appendTo(m);
var row = $('<div idx="' + d.id + '" class="ue_tree_text"></div>').appendTo(n);
n.append(row);
if (d.img) {
$('<div class="ue_tree_img"><img alt="" src="' + d.img + '"/></div>').appendTo(n);
}
row.append(d.text);
if (c.hasCreate) {
n.append('<div idx="' + d.id + '" class="ue_tree_btn ue_tree_del">-</div>');
n.append('<div idx="' + d.id + '" class="ue_tree_btn ue_tree_add">+</div>');
}
};
//公开属性
this.container = $(container);
this.selectedIdx = 0; //选中行索引
this.selectedItem = null; //选中行
this.root = null; //数据集
this.data = data; //数据源
this.config = $.extend({}, defaultConfig, config); //配置信息
this.getDataById = function (value) {
for (var i = 0; i < this.data.length; i++) {
if (this.data[i].id == value) {
return this.data[i];
}
}
return null;
};
this.bindData = function (d, c) {
this.config = c ? $.extend(defaultConfig, c) : this.config;
baseStyle(this.config);
parent.attr('idx', '0').addClass('ue_tree_container').html('').appendTo(this.container);
this.data = d || this.data;
this.root = { isLeaf: false, depth: 0, path: '0', id: 0, children:[] };
formatData(this, this.root);
addContent(this, parent, this.root);
bindEvent(this);
};
this.addChild = function(p, dt){
var tmp = this.getDataById(dt.id);
if(tmp){
alert('添加的数据主键已存在');
return false;
}
dt.parentId = p.id;
dt.depth = p.depth + 1;
dt.isLeaf = true;
dt.path = p.path + '_' + dt.id;
dt.children = [];
p.isLeaf = false;
p.children.push(dt);
this.data.push(dt);
var frame = $('.ue_tree_frame[idx="' + p.id + '"]');
if(frame.length < 1){
frame = $('<div idx="' + p.id + '" class="ue_tree_frame"></div>').appendTo($('.ue_tree_content[idx="' + p.id + '"]'));
frame.show();
var c = this.config,
f = c.flags[c.flagType],
html = c.flagType == 'addReduce' ? f.open : '<img class="ue_tree_img" alt="" src="' + f.open + '"/>';
$('.ue_tree_flag[idx="' + p.id + '"]').append(html).attr('state', 'open');
}
addItem(frame, dt, this.config);
};
this.bindData();
};
$.fn.addTree = function (data, config) {
return new UETree(this, data, config);
};
}());
var testData = [
{ id: 1, parentId: 8, text: '湖北' },
{ id: 11, parentId: 1, text: '武汉' },
{ id: 111, parentId: 11, text: '武昌' },
{ id: 112, parentId: 11, text: '汉口' },
{ id: 113, parentId: 11, text: '汉阳' },
{ id: 113, parentId: 11, text: '洪山' },
{ id: 12, parentId: 1, text: '襄樊' },
{ id: 13, parentId: 1, text: '宜昌' },
{ id: 14, parentId: 1, text: '黄石' },
{ id: 2, parentId: 8, text: '北京' },
{ id: 3, parentId: 9, text: '广东' },
{ id: 31, parentId: 3, text: '广州' },
{ id: 32, parentId: 3, text: '深圳' },
{ id: 33, parentId: 3, text: '澳门' },
{ id: 4, parentId: 9, text: '上海' },
{ id: 5, parentId: 9, text: '浙江' },
{ id: 41, parentId: 5, text: '杭州' },
{ id: 42, parentId: 5, text: '温州' },
{ id: 6, parentId: 8, text: '香港' },
{ id: 7, parentId: 9, text: '台湾' },
{ id: 71, parentId: 7, text: '高雄' },
{ id: 72, parentId: 7, text: '台北' }
];
var config = {
flagType: 'triangle',
hasCreate: true,
selectCallback: function (id, dt, item) {
alert('select');
return id;
}, //选中行事件的回调方法
deleteItemCallback: function (id, dt, item) {
alert('delete');
return true;
}, //点击删除当前节点事件的回调方法
addChildCallback: function (id, dt, item) {
alert('add');
return { id:311, parentId:31, text: '白云'};
}, //点击添加子节点事件的回调方法
//hasCheckbox: true
};
var t1 = $('#testTree').addTree(testData, config);
</script>