無限分級和tree結構數據增刪改【提供Demo下載】

無限分級

不少時候咱們不肯定等級關係的層級,這個時候就須要用到無限分級了。前端

說到無限分級,又要扯到遞歸調用了。(聽說頻繁遞歸是很耗性能的),在此咱們須要先設計好表機構,用來存儲無限分級的數據。固然,如下都是本身搗鼓的結果,非標準。誰有更好的設計望不吝嗇賜教。node

說來其實也簡單,就是一個ID和父ID的關係。ajax

以此類推,Id須要是惟一的,ParenId須要是Id列裏面存在便可。這樣咱們就實現無限分級了,若是再加一列Sort排序就更完美了。api

jstree插件

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

爲何要用這個插件?由於有方便的api給咱們作數據綁定,且支持節點拖動來實現增刪改,我的以爲這個功能挺強大的。post

Demo

下面咱們來基於jstree插件來實現無限分級數據操做。以區域數據操做爲例,用Code First的方式來編寫demo代碼。性能

建立Region實體

爲了配合插件自動生成的節點id,咱們這裏使用的Node和ParentNode來存儲上下級關係(而不是上面說的id和parentid,可是實際效果是同樣的)。ui

/// <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插件的數據要求,咱們須要把上面的數據轉換成樹狀的數據對象。 this

/// <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);
 }

以上後臺的數據差很少就完成了。url

前臺數據加載

 $(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
相關文章
相關標籤/搜索