JS Tree

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>

分类: jQuery常用插件

时间: 2024-10-07 14:03:40

JS Tree的相关文章

The way of Webpack learning (V.) -- css和js的tree shaking

一:基本概念 1.字面意思是摇树,一句话:项目中没有使用的代码会在打包时候丢掉.分为css的tree shaking和js的tree shaking. 2.使用场景:1)常规优化.2)使用第三方库,但是只使用了部分功能. 二:JS tree shaking 在webpack4中已经移除了UglifyJsPlugin,只需要配置mode为"production",即可显式激活 UglifyjsWebpackPlugin 插件. 下面说的是webpack3.10.0的实现方法: (1)常规

zone.js - 暴力之美

在ng2的开发过程中,Angular团队为我们带来了一个新的库 – zone.js.zone.js的设计灵感来源于Dart语言,它描述JavaScript执行过程的上下文,可以在异步任务之间进行持久性传递,它类似于Java中的TLS(thread-local storage: 线程本地存储)技术,zone.js则是将TLS引入到JavaScript语言中的实现框架. 那么zone.js能为我们解决什么问题呢?在回答这个问题之前,博主更希望回顾下在JavaScript开发中,我们究竟遇见了什么难题

Road to the future——伪MVVM库Q.js

模仿Vuejs的伪MVVM库,下面是使用说明 项目地址:https://github.com/miniflycn/Q.js 相关项目:https://github.com/miniflycn/Ques 一个简单例子 模版: <a href="javascript:void(0)" q-text="msg"></a> 脚本: var vm = new Q({ el: '#demo', data: { msg: 'hello' } }); 则会展

Web3D编程入门总结——WebGL与Three.js基础介绍

1 /*在这里对这段时间学习的3D编程知识做个总结,以备再次出发.计划分成“webgl与three.js基础介绍”.“面向对象的基础3D场景框架编写”.“模型导入与简单3D游戏编写”三个部分,其他零散知识以后有机会再总结.*/ 2 /*第一部分,webgl与three.js基础介绍,要求读者掌握JavaScript入门知识*/ 3 //webgl原理:通过JavaScript语言在浏览器端生成glsl代码,把glsl代码送入显卡执行,把执行结果显示在浏览器中 4 //简单例程: 5 //根据To

jquery,tree无限级树形菜单+简单实用案例

jquery,tree无限级树形菜单+简单实用案例 我在项目中用到产品类别的树形.各种地方都要用. 我就封装起来,方便以后调用. 记录下来,希望给新手们提供帮助.要记得导入jquery.js  tree.js 哦 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib prefix="c" uri=&quo

webpack4 系列教程(八): CSS Tree Shaking

教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步原文地址 有空就来看看个人技术小站, 我一直都在 0. 课程介绍和资料 本次课程的代码目录(如下图所示): >>> 本节课源码 >>> 所有课程源码 1. CSS 也有 Tree Shaking? 是滴,随着 webpack 的兴起,css 也可以进行 Tree Shaking: 以去除项目代码中用不到的 CSS 样式,仅保留被使用的样式代码. 为了方便理解 Tree Shaking 概念,并且与 JS

Three.js的开始(附代码)_2

1 下载Three.js代码 https://github.com/mrdoob/three.js/tree/master/build 2 引用方法 在HTML中添加以下代码: <script type="text/javascript" src="js/three.js"></script> 3 定义Canvas元素 手动定义Canvas元素(WebGL渲染的需要) <!DOCTYPE html> <html lang=&

【项目总结:波士顿东大校友会】CMS栏目个性化设置

开发流程完整实现: 1.实体(entity.model) (截取代码省略setter.getter) /** * 类描述:系统栏目实体 * 创建人:周磊 * 创建时间:2013-8-30 下午03:58:50 */ public class ChannelEntity { /** * 初始化信息 */ public static final String ROOT_NAME = "网站系统栏目"; public static final int ROOT_ID = 0; /** *栏目

OA 权限树搭建时的技巧

1 <%-- <s:checkboxlist name="privilegeIds" list="#privilegeList" listKey="id" listValue="name"> </s:checkboxlist> --%> <s:iterator value="#privilegeList"> <input type="check