转载

jQuery插件实例七:一棵Tree的生成史

在需要表示级联、层级的关系中,Tree作为最直观的表达方式常出现在组织架构、权限选择等层级关系中。典型的表现形试类似于:

jQuery插件实例七:一棵Tree的生成史

一颗树的生成常常包括三个部分: 1)数据库设计;2)后台程序;3)前端代码。 那么,具体是怎么样的呢?

一、数据库设计

数据库设计对于树的表达常会包含这么几个类似意思的字段:

parent_id、id、name。

id:用于描述自己;

parent_id:用于描述自己的上一级;

name:用于描述自己的名称;

例如:总办(id=3,parent_id=0,name=总办),客户服务中心(id=10,parent_id=3,name=客户服务中心) ,客户部(id=12,parent_id=10,name=客户部)。由此建立了三级层级关系。

二、后台程序

对于一个层级,可能会用于描述部门关系,还可能用于描述菜单关系等等,不同的用途有不同的数据库设计字段。但为了程序的通用性,不可能为一了一个表或功能做单独的前端插件,因此就要在后台为前端插件需要使用到的字段做一个规范(或者在数据库设计中做规范)。在此为“树结构”在后台作这样的规范:

 1     /// <summary>  2   3     /// 层级  4   5     /// </summary>  6   7     public class vmHierarchy  8   9     { 10  11         public int id { get; set; } 12  13         public int pid { get; set; } 14  15         public string name { get; set; } 16  17         public object sub { get; set; } 18  19         public int status { get; set; } 20  21 }

Pid:用于描述上级关系;

Sub:用于描述子级关系;

Status:用于描述自身状态或特殊标识;

以做部门的层级关系为例:分为两个部分:

1)  取数据:

/// <summary> /// 取部门层级 /// </summary> /// <returns></returns> public List<vmHierarchy> GetDepartmentRelation() {  List<vmHierarchy> vmdrlist = new List<vmHierarchy>();  using (var ctx = DB.ContextForName(DBConnection.DefaultConnection).UseTransaction(true))  {   List<au_Department> adlist = new List<au_Department>();   adlist = base.GetModelAll();   vmdrlist = GetDepartmentRelationSub(1, adlist);  }  return vmdrlist; } 

2) 定层级:

/// <summary> /// 取部门层级-子级 /// </summary> /// <param name="parentid"></param> /// <param name="adlist"></param> /// <returns></returns> public List<vmHierarchy> GetDepartmentRelationSub(int parentid, List<au_Department> adlist) {  List<vmHierarchy> vmdrlist = new List<vmHierarchy>();  List<au_Department> modellist = new List<au_Department>();  modellist = adlist.Where(s => s.parent_id == parentid).OrderBy(s => s.sequence).ToList<au_Department>();  foreach (au_Department item in modellist)  {   vmHierarchy vmmodel = new vmHierarchy();   vmmodel.id = item.id;   vmmodel.pid = item.parent_id;   vmmodel.name = item.name;   vmmodel.sub = GetDepartmentRelationSub(item.id, adlist);   vmdrlist.Add(vmmodel);  }  return vmdrlist; } 

由此在前端可以得到类似这样的关系数据:

jQuery插件实例七:一棵Tree的生成史

三、前端代码

在与前端代码时,关于树的逻辑关系理清是最为主要的。

1)  如何生成当前层级关系和期子级关系,每个节点的子节点都不同。

2)  需要复选框吗?

3)  需要折叠吗?

4)  当点击一个节点:

A:其下还有一串节点,要全部选中/全部不选中?

B:当前点击中其它子节点都被选中了,再选中这个节点,如何影响上级的选中与不选中?

总结为:在有复选框的情况下,如何影响它的下级和上级节点关系?

5)  三个事件:

A:单击选中复选框事件;

B:单击取消选中复选框事件;

C:单击行事件;

事件顺序?冒泡?必要事件与用户自定义事件?

有需求的童鞋可以看看下面的jQuery代码:

jQuery:

//Tree层级关系 Begin ; (function ($, window, document, undefined) {  var defaults = {   ajaxurl: '',//ajax取数据的url[data==null时有效]   data: null,//数据   erow: null,//点击行时要执行的事件function(){}   checbox: true,//是否有复选框   initunfold: true,//初始展开true 初始折叠false   event: {    selectedrows: null,//单击行时要执行的事件    checked: null,//选中了复选框时要执行的事件    unchecked: null//取消选中复选框时要执行的事件   },//事件   exchangebar: false,//是否有全部展开 全部折叠 按钮   onlyleafcheck: false//是否只有最终子节点才显示checkbox  };  $.fn.etree = function (options) {   var $that = $(this);   var _ops = $.extend(true, {}, defaults, options);   var $con = null, _activehtml;   var _lv = 0;   //初始化数据   function initdata() {    if (_ops.data !== null) {     generateTree(_ops.data);    } else {     $.ajax({      url: _ops.ajaxurl,      dataType: "JSON",      success: function (result) {       _ops.data = result;       generateTree(_ops.data);      }     });    }   };   function generateTree(_data) {    console.log(_data);    $con = $('<div></div>').appendTo($that);    var $ul = $('<ul class="e-tree-ul"></ul>').appendTo($con);    generateSub($con, _data, _lv);    initEvent();    if (_ops.initunfold == false) {     $con.find('.tge-inv').each(function () {      $(this).click();     })    }   };   function generateSub($e, _data, _lv) {    for (var i = 0; i < _data.length; i++) {     var _tdata = _data[i];     var $li = $('<li class="e-tree-li"></li>').appendTo($e);     var $p = $('<p class="e-tree-p" lv=' + _lv + '></p>').appendTo($li);     var $ti = $('<i class="tge-inv"></i>').appendTo($p);     var $tif = $('<i class="tge-invf"></i>').appendTo($p);     var $tc = null;     if (_ops.checbox == true) {      if (_ops.onlyleafcheck == true && _tdata.sub.length == 0) {       $tc = $('<i class="sck" tid="' + _tdata.id + '"></i>').appendTo($p);      } else if (_ops.onlyleafcheck == false) {       $tc = $('<i class="sck" tid="' + _tdata.id + '"></i>').appendTo($p);      }     }     var $ts = $('<span class="e-tree-s"></span>').html(_tdata.name).appendTo($p);     if (_tdata.sub.length > 0) {      $tif.addClass('tge-invfr');      var $ul = $('<ul class="e-tree-ul"></ul>').appendTo($li);      generateSub($ul, _tdata.sub, (_lv + 1));      $ti.addClass('tge-invd');     } else {      $tif.addClass('tge-invfd');     }     if ($tc != null) {      if (_tdata.status == 1) {       $tc.addClass('ck');//选中      } else {       $tc.addClass('nock');//未选中      }     }    }   };   function checksubordinate($e) {    var $slv = $e.parent('p').next('ul');    if ($e.hasClass('ck')) {     $slv.find('.sck').removeClass('nock').addClass('ck');    } else if ($e.hasClass('nock')) {     $slv.find('.sck').removeClass('ck').addClass('nock');    }   };   function checksuperior($e) {    var $plv = $e.parent('p').parent('li').parent('ul');    if ($plv.length > 0) {     var $sib = $plv.children('li');     var $sumckdcount = $sib.children('p').children('.ck').length;     var $scount = $sib.length - $sumckdcount;     var $ppsck = $plv.prev('p').children('.sck');     if ($scount == 0) {      $plv.prev('p').children('.sck').removeClass('nock').addClass('ck');     } else {      $plv.prev('p').children('.sck').removeClass('ck').addClass('nock');     }     if ($ppsck.length > 0) {      checksuperior($ppsck);     }    }   };   function checkselect($e) {    checksuperior($e);    checksubordinate($e);   };   function setAction($e) {    var $ts = $e;    var $te = $ts.parent('p');    var $thisid = parseInt($e.attr('tid'));    $con.find('.e-tree-active').removeClass('e-tree-active');    if ($ts.hasClass('ck')) {     $ts.removeClass('ck').addClass('nock');     checkselect($ts);     if (typeof _ops.event.unchecked == "function") {      _ops.event.unchecked($te, iselectedhtml());//活动项,唯一选中项|null     }    } else if ($ts.hasClass('nock')) {     $ts.removeClass('nock').addClass('ck');     checkselect($ts);     if (_ops.event.checked != null) {      _ops.event.checked($te, iselectedhtml());//活动项,唯一选中项|null     }    }    var $shtml = iselectedhtml();    if ($shtml != null) {     var $ck = $shtml.children('.ck');     var $sid = parseInt($shtml.attr('tid'));     $ck.parent('p').addClass('e-tree-active');     if ($thisid == $sid) {      setAction($ck);     }    }   };   function initEvent() {    $con.find('.tge-inv').bind('click', function (e) {     var $ts = $(this);     var $next = $ts.parent('p').next();     var $tsnext = $ts.next();     if ($ts.hasClass('tge-invd')) {      $ts.removeClass('tge-invd').addClass('tge-invr');      $next.slideUp();      if ($tsnext.hasClass('tge-invfr')) {       $tsnext.removeClass('tge-invfr').addClass('tge-invfd');      }     } else if ($ts.hasClass('tge-invr')) {      $ts.removeClass('tge-invr').addClass('tge-invd');      $next.slideDown();      if ($tsnext.hasClass('tge-invfd')) {       $tsnext.removeClass('tge-invfd').addClass('tge-invfr');      }     }     e.stopPropagation();    });    $con.find('.sck').bind('click', function (e) {     var $ts = $(this);     setAction($ts);     e.stopPropagation();    });    $con.find('.e-tree-p').bind('click', function () {     $(this).children('.sck').click();    });    if (typeof _ops.event.selectedrows == "function") {     $con.find('.e-tree-p').bind('click', function () {      var $te = $(this).context;      _ops.event.selectedrows($($te), iselectedhtml());//活动项,唯一选中项|null     });    }    $con.find('.e-tree-s').bind('click', function () {     return false;    });   };   function iactivehtml() {    _activehtml = $con.find('.e-tree-active').html();    return _activehtml;   };   function iactiveid() {    var $thtml = $con.find('.e-tree-active');    _activeid = parseInt($thtml.find('.sck').attr('tid'));    return _activeid;   };   function iselectedids() {    var _ids = new Array();    $con.find('.ck').each(function () {     _ids.push(parseInt($(this).attr('tid')));    });    return _ids;   };   function iselectedhtml() {    if (iselectedids().length == 1) {     return $con.find('.ck').parent('p');    } else {     return null;    }   };   function iselectedid() {    if (iselectedids().length == 1) {     return parseInt($con.find('.ck').attr('tid'));    } else {     return null;    }   };   initdata();   //活动项html [活动项:当前点击的项]   this.activehtml = function () {    return iactivehtml();   };   //活动项id [活动项:当前点击的项]   this.activeid = function () {    return iactiveid();   };   //获取所有选中的项id   this.selectedids = function () {    return iselectedids();   };   //当前唯一选中项的html 不满足唯一选中时,返回null   this.selectedhtml = function () {    return iselectedhtml();   };   //当前唯一选中项的id 不满足唯一选中时,返回null   this.selectedid = function () {    return iselectedid();   };   return this;  }; })(jQuery, window, document); //Tree层级关系 End 

使用:

jQuery插件实例七:一棵Tree的生成史
@{  Layout = null; } @using UCMS_Commons; @using UCMS_Model; @using UCMS_Model.ViewModel; <!DOCTYPE html> <html> <head>  <meta name="viewport" content="width=device-width" />  <title>DepatmentManage</title>  <link href="~/Content/themes/black/Css/eui.css" rel="stylesheet" />  <script src="~/Scripts/jquery-1.8.2.min.js"></script>  <script src="~/Scripts/extendjs/jquery.cookie.js"></script>  <script src="~/Content/themes/black/Script/jquery.eui.js"></script>  <style type="text/css">   #dp-content {    width: 920px;    margin: 0 auto;   }   #de-cont {    width: 400px;    border: 1px solid #E4E4E4;    padding: 20px;    display: inline-block;    vertical-align: top;   }   #oper-cont {    width: 400px;    border: 1px solid #E4E4E4;    padding: 20px;    display: inline-block;    vertical-align: top;    margin-left: 28px;   }   fieldset {    border: 1px solid #ddd;   }   legend {    color: #9b9b9b;   }  </style>  <script type="text/javascript">   $(function () {    var actionUrl = {     'GetDpInfo': '/SystemCenter/GetDpInfo',     'adddp': '/SystemCenter/AddDp',     'deletedp': '/SystemCenter/DeleteDp',     'updatedp': '/SystemCenter/UpdateDp'    };    var t = JSON.parse('@Html.Raw(JSONNet.Serialize(Model))');    var $opername = $('#oper-name');    var $opersub = $('#oper-cont-sub');    var $operinfoo = $('#oper-info-o');    var $operinfoname = $('#oper-info-name');    var $errormsg = $('#error-msg');    var $deletedp = $('#deletedp');    var $dpname = $('#dpname');    var _status = -1, _ttname = '';    $('input[name="opertype"]').bind('click', function () {     var _index = parseInt($(this).attr('tp'));     $operinfoo.show();     switch (_index) {      case 0: { $opername.attr('readonly', 'readonly'); $opersub.show(); $opername.val(_ttname); $('#oper-info-o').show(); }; break;      case 1: { $opername.removeAttr('readonly'); $opersub.hide(); $opername.focus().val(''); _status = 1; }; break;      case 2: { $opername.removeAttr('readonly'); $opersub.hide(); $opername.focus().val(''); _status = 2; }; break;      case 3: { $opername.removeAttr('readonly'); $opersub.hide(); $opername.focus(); _status = 3; }; break;     }    });    $('#savedp').bind('click', function () {     switch (_status) {      case 1: { adddp(1); }; break;      case 2: { adddp(2); }; break;      case 3: { updatedp(); }; break;     }    });    $('#deletedp').bind('click', function () {     deletedp();    });    function adddp(_type) {     var _id = _dptree.activeid();     var _dpname = $opername.val();     if (_dpname.length == 0) {      $errormsg.html('请填写 名称');     }     else {      $.ajax({       url: actionUrl.adddp,       data: { "thisid": _id, "addtype": _type == 1 ? 0 : 1, "addname": _dpname },       success: function (result) {        var _result = $.eui.checkresult(result);        if (_result) {         window.location.reload();        }       }      });     }    };    function deletedp() {     var _ids = _dptree.selectedids();     var _idarray = JSON.stringify(_ids);     $.ajax({      url: actionUrl.deletedp,      data: { "ids": _idarray },      success: function (result) {       var _result = $.eui.checkresult(result);       if (_result) {        window.location.reload();       }      }     });    };    function updatedp() {     var _id = _dptree.activeid();     var _name = $opername.val();     if (_name.length == 0) {      $errormsg.html('请填写 名称');     }     else {      $.ajax({       url: actionUrl.updatedp,       data: { "thisid": _id, "newname": _name },       success: function (result) {        var _result = $.eui.checkresult(result);        if (_result) {         window.location.reload();        }       }      });     }    };    function getdpinfo(_id) {     $.ajax({      url: actionUrl.GetDpInfo,      data: { "id": _id },      success: function (result) {       var $os = $('#oper-sub').html('');       for (var i = 0; i < result.length; i++) {        $('<span style="padding: 0 10px;"></span>').html(result[i].name + '(' + result[i].percount + '人)').appendTo($os);       }      }     });    };    function schecked(el, sl) {     $('input[name="opertype"]').eq(0).click();     if (sl != null) {      var _name = sl.find('.e-tree-s').html();      $dpname.html(_name);      $opername.val(_name);      _ttname = _name;      getdpinfo(_dptree.selectedid());      $operinfoo.show();     } else {      $dpname.html('已选中个数:' + _dptree.selectedids().length);      $operinfoo.hide();     }    };    var _dptree = $('#de-cont-d').etree({     data: t,     checbox: true,     onlyleafcheck: true,     event: {      selectedrows: function (el, sl) {      },      checked: function (el, sl) {       var _tname = el.find('.e-tree-s').html();       $deletedp.show();       $errormsg.html('');       schecked(el, sl);      },      unchecked: function (el, sl) {       $operinfoo.hide();       schecked(el, sl);      }     }    });   });  </script> </head> <body>  <div id="dp-content">   <fieldset id="de-cont">    <legend>部门</legend>    <div id="de-cont-d">    </div>   </fieldset>   <fieldset id="oper-cont">    <legend>信息</legend>    <div id="oper-info-name" style="line-height: 32px;">     名称:<span id="dpname"></span>     <a href="javascript:void(0);" class="eui-btns" style="float:right;display:none;" id="deletedp">删除?</a>    </div>    <div id="oper-info-o" style="display:none;">     <hr class="hrgrey" />     <p id="oper-cont-d" class="eui-p50">      操作类别:      <label><input name="opertype" type="radio" value="" tp="0" checked="checked" />查看信息</label>      <label><input name="opertype" type="radio" value="" tp="1" />添加同级</label>      <label><input name="opertype" type="radio" value="" tp="2" />添加子级</label>      <label><input name="opertype" type="radio" value="" tp="3" />修改自身</label>     </p>     <p class="eui-p50">      名称:      <input type="text" name="name" value="" id="oper-name" class="eui-input" readonly="readonly" />     </p>     <p class="eui-p50" id="oper-cont-sub">      下辖职位:<span id="oper-sub"></span>     </p>     <label id="error-msg" class="error-msg"></label>     <a href="javascript:void(0);" class="eui-btns" style="float:right;bottom:0;" id="savedp">保存</a>    </div>   </fieldset>  </div> </body> </html> 
View Code
正文到此结束
Loading...