zoukankan      html  css  js  c++  java
  • jQuery插件实例七:一棵Tree的生成史

    在需要表示级联、层级的关系中,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;
    
            }

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

     

    三、前端代码

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

    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

    使用:

    @{
        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
  • 相关阅读:
    例题6-8 Tree Uva548
    例题6-7 Trees on the level ,Uva122
    caffe Mac 安装
    Codeforces Round #467 (Div. 1) B. Sleepy Game
    Educational Codeforces Round37 E
    Educational Codeforces Round 36 (Rated for Div. 2) E. Physical Education Lessons
    Good Bye 2017 E. New Year and Entity Enumeration
    Good Bye 2017 D. New Year and Arbitrary Arrangement
    Codeforces Round #454 D. Seating of Students
    浙大紫金港两日游
  • 原文地址:https://www.cnblogs.com/nhsd/p/4472882.html
Copyright © 2011-2022 走看看