zoukankan      html  css  js  c++  java
  • 无限分级和tree结构数据增删改【提供Demo下载】

    无限分级

    很多时候我们不确定等级关系的层级,这个时候就需要用到无限分级了。

    说到无限分级,又要扯到递归调用了。(据说频繁递归是很耗性能的),在此我们需要先设计好表机构,用来存储无限分级的数据。当然,以下都是自己捣鼓的结果,非标准。谁有更好的设计望不吝啬赐教。

    说来其实也简单,就是一个ID和父ID的关系。

    以此类推,Id需要是唯一的,ParenId需要是Id列里面存在即可。这样我们就实现无限分级了,如果再加一列Sort排序就更完美了。

    jstree插件

    官方地址:https://www.jstree.com/

    为什么要用这个插件?因为有方便的api给我们做数据绑定,且支持节点拖动来实现增删改,个人觉得这个功能挺强大的。

    Demo

    下面我们来基于jstree插件来实现无限分级数据操作。以区域数据操作为例,用Code First的方式来编写demo代码。

    创建Region实体

    为了配合插件自动生成的节点id,我们这里使用的Node和ParentNode来存储上下级关系(而不是上面说的id和parentid,但是实际效果是一样的)。

    /// <summary>
    /// 区域
    /// </summary>
    public class Region
    {
        /// <summary>
        /// 主键id
        /// </summary>
        public int Id { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 节点
        /// </summary>
        public string Node { get; set; }
        /// <summary>
        /// 父节点
        /// </summary>
        public string ParentNode { get; set; }
    
    }

    满足jstree插件的数据对象Dto

    为了适应jstree插件的数据要求,我们需要把上面的数据转换成树状的数据对象。 

    /// <summary>
    /// Dto
    /// </summary>
    public class RegionsTreeOutput
    {
        /// <summary>
        /// Id
        /// </summary>
        public int RegionsId { get; set; }
        /// <summary>
        /// tree显示文本(对应region的name)
        /// </summary>
        public string text { get; set; }
        /// <summary>
        /// tree的id(对应Node)
        /// </summary>
        public string id { get; set; }      
        /// <summary>
        /// 子节点数据(此属性就体现的数据的层级关系)
        /// </summary>
        public List<RegionsTreeOutput> children { get; set; }
    }
    View Code

    数据转换

      #region GetRegionTree 初始化数据获取 的辅助方法
            public RegionsTreeOutput LoadRegions(string id, List<Region> inRegions, RegionsTreeOutput outRegions)
            {
    
                List<Region> regions = inRegions.Where(t => t.ParentNode == id).ToList();
                if (outRegions == null)//加载父节点
                {
                    outRegions = ToTreeData(regions[0]);
                    LoadRegions(outRegions.id, inRegions, outRegions);
                }
                else//加载子节点
                {
                    outRegions.children = ToTreesData(regions);
                    if (regions.Count > 0)
                    {
                        for (int i = 0; i < regions.Count; i++)
                        {
                            LoadRegions(regions[i].Node, inRegions, outRegions.children[i]);//递归调用
                        }
                    }
                }
                return outRegions;
            }
    
            public RegionsTreeOutput ToTreeData(Region region)
            {
                var treeData = new RegionsTreeOutput();
                treeData.id = region.Node;
                treeData.text = region.Name;
                treeData.RegionsId = region.Id;            
                return treeData;
            }
            public List<RegionsTreeOutput> ToTreesData(List<Region> listRegion)
            {
                var regions = new List<RegionsTreeOutput>();
                for (int i = 0; i < listRegion.Count; i++)
                {
                    regions.Add(ToTreeData(listRegion[i]));
                }
                return regions;
            }
            #endregion
    View Code

    初始化获取转换后的数据

     /// <summary>
     /// 初始化数据获取
     /// </summary>
     /// <returns></returns>
     public JsonResult GetResultData()
     {
         TreeDbContext db = new TreeDbContext();
         var regions = db.Regions.Where(t => true).ToList();
         var regionObj = LoadRegions("-1", regions, null);
         return Json(regionObj);
     }

    以上后台的数据差不多就完成了。

    前台数据加载

     $(function () {
                $.post("/Home/GetResultData", null, function (sData) {
                    treeObj = $('#jstree_demo').jstree({
                        //, "checkbox"
                        'plugins': ["contextmenu", "dnd", "search", "state", "types", "wholerow"],
                        'core': {
                            "animation": 0,
                            "check_callback": true,
                            'force_text': true,
                            "themes": { "stripes": true },
                            'data': sData
                        },
                        "types": {
                            "default": {
                                "icon": "fa fa-folder icon-state-warning icon-lg"
                            },
                            "file": {
                                "icon": "fa fa-file icon-state-warning icon-lg"
                            }
                        },
                        "contextmenu": {
                            select_node: false,
                            show_at_node: true,
                            items: function (o, cb) {
                                //因为这里我们之后需要定义多个项,所以通过对象的方式返回
                                var actions = {};
                                //添加一个"新增"右键菜单
                                actions.create = {//这里的create其实阔以随意命名,关键是里面的 这里面的 action回调方法
                                    "separator_before": false,//Create这一项在分割线之前
                                    "separator_after": true,//Create这一项在分割线之后
                                    "_disabled": false, //false表示 create 这一项可以使用; true表示不能使用
                                    "label": "新增",  //Create这一项的名称 可自定义
                                    "action": function (data) {  //点击Create这一项触发该方法,这理还是蛮有用的
                                        var inst = $.jstree.reference(data.reference),
                                             obj = inst.get_node(data.reference);//获得当前节点,可以拿到当前节点所有属性
                                        //新加节点,以下三行代码注释掉就不会添加节点
                                        inst.create_node(obj, {}, "last", function (new_node) {
                                            setTimeout(function () { inst.edit(new_node); }, 0);//新加节点后触发 重命名方法,即 创建节点完成后可以立即重命名节点
                                        });
                                    }
                                };
                                if (o.id != "0001")//屏蔽对根节点的操作  “0001”改成根节点对应的真是id
                                {
                                    //添加一个"重命名"右键菜单
                                    actions.rename = {
                                        "separator_before": false,
                                        "separator_after": false,
                                        "_disabled": false, //(this.check("rename_node", data.reference, this.get_parent(data.reference), "")),
                                        "label": "重命名",
                                        "action": function (data) {
                                            var inst = $.jstree.reference(data.reference),
                                                    obj = inst.get_node(data.reference);
                                            inst.edit(obj);
                                        }
                                    }
                                    //添加一个"删除"右键菜单
                                    actions.delete = {
                                        "separator_before": false,
                                        "icon": false,
                                        "separator_after": false,
                                        "_disabled": false, //(this.check("delete_node", data.reference, this.get_parent(data.reference), "")),
                                        "label": "删除",
                                        "action": function (data) {
                                            var inst = $.jstree.reference(data.reference),
                                                    obj = inst.get_node(data.reference);
                                            if (inst.is_selected(obj)) {
                                                inst.delete_node(inst.get_selected());
                                            }
                                            else {
                                                inst.delete_node(obj);
                                            }
                                        }
                                    };
                                }
                                return actions;//返回右键菜单项
                            }
                        },
                    });
                });
            });
    View Code

    其他操作

    //删除节点
    $('#jstree_demo').on('delete_node.jstree', function (e, data) {            
                var id = data.node.original.RegionsId;          
                $.ajax({
                    type: "get",
                    url: "/Home/DeleteRegion?id=" + id,
                    success: function (sData) {
    
                    }
                }); 
            });
    //移动节点
    $('#jstree_demo').on('move_node.jstree', function (e, data) {
                saveRegions(data);
            });
    //修改名
    $('#jstree_demo').on('rename_node.jstree', function (e, data) {            
                saveRegions(data);
            });
    //保存
    function saveRegions(data) {            
                var id = data.node.original.RegionsId;
                var name = data.node.text;//修改后的name
                //var oldName = data.old;//原name           
                //var pNode = $('#jstree_demo').jstree().get_node(data.node.parent).original.RegionsId;
                var josnData = { "Id": id, "Node": data.node.id, "ParentNode": data.node.parent, "Name": name };
                $.ajax({
                    url: "/Home/SaveRegions",
                    data: josnData,
                    success: function (sData) {                   
                        data.node.original.RegionsId = sData;
                        data.node.state.opened = false;//是否展开
                    }
                });
            }

    当然,记得修改或是删除要取RegionsId这个对应后台实体的ID。

    通过按钮来操作增删改

    function createTree() {   
        var ref = $('#jstree_demo').jstree(true),
            sel = ref.get_selected();
        if (!sel.length) { return false; }
        sel = sel[0];
        sel = ref.create_node(sel, { "type": "file" });
        if (sel) {
            ref.edit(sel);
        }
    };
    
    function renameTree() {
        var ref = $('#jstree_demo').jstree(true),
            sel = ref.get_selected();
        if (!sel.length) { return false; }
        sel = sel[0];
        ref.edit(sel, function () {
            
        });
    };
    
    function deleteTree() {
        var ref = $('#jstree_demo').jstree(true),
            sel = ref.get_selected();
        if (!sel.length) { return false; }
        ref.delete_node(sel);
    };

    更加详细的细节请看demo。

    链接:http://pan.baidu.com/s/1hrN5QvU 密码:c6b7


     2016.08.26更新

    以上方式有问题:如果多个用户同时新建节点,会有重复的。(因为节点data.node.id是前端页面自动生成的) 

    解决方法:(更改data.node.id的值从后台赋值)

    前端:

    $(function () {
                treeObj = $('#jstree_demo').jstree({
                    //, "checkbox"
                    'plugins': ["contextmenu", "dnd", "search", "state", "types", "wholerow"],
                    'core': {
                        "animation": 0,
                        "check_callback": true,
                        'force_text': true,
                        "themes": { "stripes": true },
                        //修改点(1)  【可以给插件刷新】
                        'data': function (obj, callback) {
                            $.ajax({
                                "type": "post",
                                "url": "/Home/GetResultData",
                                "data": {},
                                "success": function (sData) {
                                    callback(sData);//回调传请求得到的数据(这里可以把数据组装成插件需要的数据格式)
                                }
                            });
                        }
                    },
    //保存
    function saveRegions(data) {
        var id = data.node.original.RegionsId;
        var name = data.node.text;//修改后的name
        //var oldName = data.old;//原name
        //var pNode = $('#jstree_demo').jstree().get_node(data.node.parent).original.RegionsId;
        //修改点(2)   【取自定义的父节点】
        var ParentNode = ($('#jstree_demo').jstree().get_node(data.node.parent).original.MyNode || data.node.parent);// data.node.parent;
        //修改点(3)   【取自定义的节点】
        var pNode = (data.node.original.MyNode || data.node.id);
        var josnData = { "Id": id, "Node": pNode, "ParentNode": ParentNode, "Name": name };
        $.ajax({
            url: "/Home/SaveRegions",
            data: josnData,
            type: "post",
            success: function (sData) { 
                data.node.original.RegionsId = sData.Id;
                //data.node.state.opened = true;//是否展开
                data.node.original.MyNode = sData.Node;
    
                //修改点(4)
                $.jstree.reference("#jstree_demo").refresh();//刷新jstree控件,重新加载数据()
    
            }
        });
    } 

     后台:

    input.Node = Guid.NewGuid().ToString();

     更新demo:http://pan.baidu.com/s/1gfdmQoN

    【注意】

    • 不能使用select控件,可以使用div、input
  • 相关阅读:
    Android ADB 基本命令
    Android Studio修改包名
    Android开发之SurfaceView
    Android呼吸灯效果实现
    Android xml 绘制图形
    设计模式--代理模式C++实现
    设计模式--建造者模式C++实现
    设计模式--模板方法模式C++实现
    设计模式--抽象工厂模式C++实现
    设计模式---工厂方法C++实现
  • 原文地址:https://www.cnblogs.com/zhaopei/p/5470672.html
Copyright © 2011-2022 走看看