利用 zTree 在 MVC 下實現樹型結構管理

zTree 是一個依靠 jQuery 實現的多功能 「樹插件」。優異的性能、靈活的配置、多種功能的組合是 zTree 最大優勢。你們能夠從 zTree 的官網下載和學習。javascript

我這裏經過本身的實踐,簡單介紹一下在.net MVC 環境下,如何實現 ztree 展現和管理樹型結構的。php

首先咱們先假設一下任務,管理新聞的分類,先介紹一下數據結構。css

咱們仍是先創建一個表結構對象和訪問控制類html

//新聞分類(VON_Catalog)
#region "新聞分類信息類聲明"
namespace VonPortal.Web.Models
{
    /// <summary>新聞分類 信息類</summary>
    public class CatalogInfo
    {
        #region "Constructors"
        /// <summary>
        /// 構造函數
        /// </summary>
        public CatalogInfo()
        {
        }
        /// <summary>
        /// 含初始化構造函數
        /// </summary>
        /// <param name="ID">序號</param>
        /// <param name="CatalogName">分類名稱</param>
        /// <param name="PID">父類序號</param>
        /// <param name="DisplayOrder">顯示序號</param>
        /// <param name="Note">分類說明</param>
        public CatalogInfo(int ID, string CatalogName, int PID, int DisplayOrder, string Note)
        {
            this.ID = ID;
            this.CatalogName = CatalogName;
            this.PID = PID;
            this.DisplayOrder = DisplayOrder;
            this.Note = Note;
        }
        #endregion
        #region "Public Properties"
        /// <summary>序號</summary>
        [Required]
        [Display(Name = "序號")]
        public int ID { get; set; }
        /// <summary>分類名稱</summary>
        [Display(Name = "分類名稱")]
        public string CatalogName { get; set; }
        /// <summary>父類序號</summary>
        [Required]
        [Display(Name = "父類序號")]
        public int PID { get; set; }
        /// <summary>顯示序號</summary>
        [Display(Name = "顯示序號")]
        public int DisplayOrder { get; set; }
        /// <summary>分類說明</summary>
        [Display(Name = "分類說明")]
        public string Note { get; set; }
        #endregion
    }
}
#endregion
#region "新聞分類信息基礎控制類聲明"
namespace VonPortal.Web.Operators
{
    /// <summary>新聞分類 控制類</summary>
    public class CatalogCtrl
    {
        private CatalogDataProvider dataProvider = null;
        /// <summary>啓動數據庫事務</summary>
        public IDbTransaction BeginTrans()
        {
            dataProvider = CatalogDataProvider.CreateProvider();
            return dataProvider.DBBeginTrans();
        }
        /// <summary>含數據庫事務的構造函數</summary>
        public CatalogCtrl(IDbTransaction DBTrans)
        {
            if (DBTrans == null)
                dataProvider = CatalogDataProvider.Instance();
            else
            {
                dataProvider = CatalogDataProvider.CreateProvider();
                dataProvider.DBTrans = DBTrans;
            }
        }
        //Read data and write to CatalogInfo class
        private void setInfoValue(IDataReader reader, CatalogInfo info)
        {
            info.ID = reader.GetInt32(0);  //序號
            info.CatalogName = reader.GetString(1);  //分類名稱
            info.PID = reader.GetInt32(2);  //父類序號
            info.DisplayOrder = reader.GetInt32(3);  //顯示序號
            info.Note = reader.GetString(4);  //分類說明
        }
        /// <summary>檢驗Catalog信息</summary>
        public string Check(CatalogInfo info)
        {
            string errInfo = "";
            return errInfo;
        }
        /// <summary>獲得本節點的下屬節點新的序號</summary>
        public int NewIdx(int PID)
        {
            return dataProvider.GetLasterOrder(PID) + 1;
        }
        /// <summary>
        /// 根據主鍵 PK_Catalog 提取信息
        /// </summary>
        /// <param name="ID">序號</param>
        public CatalogInfo GetByCatalog(int ID)
        {
            IDataReader reader = dataProvider.GetByCatalog(ID);
            if (!reader.Read())
            {
                reader.Close();
                return null;
            }
            CatalogInfo info = new CatalogInfo();
            setInfoValue(reader, info);
            reader.Close();
            return info;
        }
        /// <summary>獲得全部信息</summary>
        public List<CatalogInfo> List()
        {
            List<CatalogInfo> list = new List<CatalogInfo>();
            IDataReader reader = dataProvider.List();
            while (reader.Read())
            {
                CatalogInfo info = new CatalogInfo();
                setInfoValue(reader, info);
                list.Add(info);
            }
            reader.Close();
            return list;
        }
        /// <summary>根據主鍵 IDX_Catalog 提取信息</summary>
        /// <param name="PID">父類序號</param>
        public List<CatalogInfo> ListByCatalog(int PID)
        {
            List<CatalogInfo> list = new List<CatalogInfo>();
            IDataReader reader = dataProvider.ListByCatalog(PID);
            while (reader.Read())
            {
                CatalogInfo info = new CatalogInfo();
                setInfoValue(reader, info);
                list.Add(info);
            }
            reader.Close();
            return list;
        }
        /// <summary>保存Catalog信息</summary>
        ///<param name="info">信息類</param>
        public bool Save(CatalogInfo info)
        {
            info.ID = dataProvider.Save(info.ID, info.CatalogName, info.PID, info.DisplayOrder, info.Note);
            return info.ID > 0;
        }
        /// <summary>添加Catalog信息</summary>
        ///<param name="info">信息類</param>
        public int Add(CatalogInfo info)
        {
            info.ID = dataProvider.Add(info.CatalogName, info.PID, info.DisplayOrder, info.Note);
            return info.ID;
        }
        /// <summary>修改Catalog信息</summary>
        ///<param name="info">信息類</param>
        public bool Edit(CatalogInfo info)
        {
            return dataProvider.Edit(info.ID, info.CatalogName, info.PID, info.DisplayOrder, info.Note) > 0;
        }
        /// <summary>根據PK_Catalog刪除Catalog信息</summary>
        /// <param name="ID">序號</param>
        public int Del(int ID)
        {
            return dataProvider.Del(ID);
        }

    }
}
#endregion
#region "新聞分類信息操做控制類聲明"
namespace VonPortal.Web.Tasks
{
    /// <summary>新聞分類 控制類</summary>
    public class CatalogTask : CatalogCtrl
    {
        /// <summary>含數據庫事務的構造函數</summary>
        public CatalogTask(IDbTransaction DBTrans) : base(DBTrans)
        {
        }
        /// <summary>
        /// 根據主鍵 PK_Catalog 提取信息
        /// </summary>
        /// <param name="ID">序號</param>
        public new Task<CatalogInfo> GetByCatalog(int ID)
        {
            return Task.Run(() =>
            {
                return base.GetByCatalog(ID);
            });
        }
        /// <summary>根據主鍵 IDX_Catalog 提取信息</summary>
        /// <param name="PID">父類序號</param>
        public new Task<List<CatalogInfo>> ListByCatalog(int PID)
        {
            return Task.Run(() =>
            {
                return base.ListByCatalog(PID);
            });
        }
        /// <summary>保存Catalog信息</summary>
        ///<param name="info">信息類</param>
        public new Task<bool> Save(CatalogInfo info)
        {
            return Task.Run(() =>
            {
                return base.Save(info);
            });
        }
        /// <summary>添加Catalog信息</summary>
        ///<param name="info">信息類</param>
        public new Task<int> Add(CatalogInfo info)
        {
            return Task.Run(() =>
            {
                return base.Add(info);
            });
        }
        /// <summary>修改Catalog信息</summary>
        ///<param name="info">信息類</param>
        public new Task<bool> Edit(CatalogInfo info)
        {
            return Task.Run(() =>
            {
                return base.Edit(info);
            });
        }
        /// <summary>根據PK_Catalog刪除Catalog信息</summary>
        /// <param name="ID">序號</param>
        public new Task<int> Del(int ID)
        {
            return Task.Run(() =>
            {
                return base.Del(ID);
            });
        }

    }
}
#endregion
#region "新聞分類信息數據庫訪問基類聲明"
namespace VonPortal.Web.Data
{
    /// <summary>
    /// 數據及操做控制層
    /// <seealso cref="VonPortal.Web.Business.CatalogInfo"/>
    /// <seealso cref="VonPortal.Web.Business.CatalogCtrl"/>
    /// </summary>
    public abstract class CatalogDataProvider : DataProvider
    {
        #region Shared/Static Methods
        // singleton reference to the instantiated object 
        private static CatalogDataProvider objProvider = null;
        /// <summary>
        /// constructor
        /// </summary>
        static CatalogDataProvider()
        {
            objProvider = CreateProvider();
        }
        /// <summary>
        /// dynamically create provider
        /// </summary>
        /// <returns>return the provider</returns>
        public static CatalogDataProvider CreateProvider()
        {
            return (CatalogDataProvider)VonPortal.Web.Reflection.CreateDataProvider("von", "NewsModule", "VonPortal.Web.Data.CatalogDataProvider");
        }
        /// <summary>
        /// The instance of CatalogDataProvider.
        /// </summary>
        /// <returns>return the provider</returns>
        public static CatalogDataProvider Instance()
        {
            if (objProvider == null) objProvider = CreateProvider();
            return objProvider;
        }
        #endregion

        #region "Catalog Abstract Methods"
        /// <summary>根據主鍵 PK_Catalog 提取信息</summary>
        public abstract IDataReader GetByCatalog(int ID);
        /// <summary>獲得本節點的最後節點序號</summary>
        public abstract int GetLasterOrder(int PID);
        /// <summary>根據主鍵 IDX_Catalog 提取信息</summary>
        public abstract IDataReader ListByCatalog(int PID);
        /// <summary>提取所有信息</summary>
        public abstract IDataReader List();
        /// <summary>保存Catalog信息</summary>
        public abstract int Save(int ID, string CatalogName, int PID, int DisplayOrder, string Note);
        /// <summary>添加Catalog信息</summary>
        public abstract int Add(string CatalogName, int PID, int DisplayOrder, string Note);
        /// <summary>修改Catalog信息</summary>
        public abstract int Edit(int ID, string CatalogName, int PID, int DisplayOrder, string Note);
        /// <summary>根據PK_Catalog刪除Catalog信息</summary>
        public abstract int Del(int ID);
        #endregion
    }
}
#endregion

最後的VonPortal.Web.Data.CatalogDataProvider是數據庫訪問接口類,您能夠根據本身的實際需求創建真實的數據庫訪問類,完成真正的數據庫訪問,我這裏就不在詳述了,下面將介紹今天的主角 zTree。你們能夠經過 zTree 網上 API 來深刻了解其代碼編寫規則,也能夠下載 Demo 來進行研究。java

首先按照常規咱們先創建後臺處理的Model和Controller。node

using System.ComponentModel.DataAnnotations;
//新聞分類(VON_Catalog)
namespace VonPortal.Web.Modules
{
    public class CatalogModel
    {

        /// <summary>序號</summary>
        [Required]
        [Display(Name = "序號")]
        public int ID { get; set; }
        /// <summary>分類名稱</summary>
        [Display(Name = "分類名稱")]
        public string CatalogName { get; set; }
        /// <summary>父類序號</summary>
        [Required]
        [Display(Name = "父類序號")]
        public int PID { get; set; }
        /// <summary>顯示序號</summary>
        [Display(Name = "顯示序號")]
        public int DisplayOrder { get; set; }
        /// <summary>分類說明</summary>
        [Display(Name = "分類說明")]
        public string Note { get; set; }
    }
}

using System.Collections.Generic;
using System.Web.Mvc;
using VonPortal.Web.Models;
using VonPortal.Web.Operators;

namespace VonPortal.Web.Modules.Controllers
{
    public class NewsController : Controller
    {
        public ActionResult NewsCatalogManager()
        {
            return PartialView();
        }
        /// <summary>
        /// ajax 添加一個新聞分類
        /// </summary>
        /// <param name="PID">上級序號</param>
        /// <param name="CatalogName">分類名稱</param>
        /// <param name="Note">分類說明</param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult NewCatalog(int PID, string CatalogName, string Note)
        {
            CatalogCtrl ctrl = new CatalogCtrl(null);
            CatalogInfo info = new CatalogInfo(0, CatalogName, PID, 0, Note);
            info.DisplayOrder = ctrl.NewIdx(PID);
            ctrl.Add(info);
            return Json(new { bRet = true, sMsg = "添加成功", html = info.ID.ToString() }, "text/html");
        }
        /// <summary>
        /// ajax 修改一個分類信息
        /// </summary>
        /// <param name="PID">上級序號</param>
        /// <param name="CatalogName">分類名稱</param>
        /// <param name="Note">分類說明</param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult EditCatalog(int ID, string CatalogName, string Note)
        {
            CatalogCtrl ctrl = new CatalogCtrl(null);
            CatalogInfo info = ctrl.GetByCatalog(ID);
            info.CatalogName = CatalogName;
            info.Note = Note;
            ctrl.Edit(info);
            return Json(new { bRet = true, sMsg = "修改爲功" }, "text/html");
        }
        /// <summary>
        /// ajax 刪除一個新聞分類
        /// </summary>
        /// <param name="ID">分類序號</param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult DeleteCatalog(int ID)
        {
            CatalogCtrl ctrl = new CatalogCtrl(null);
            ctrl.Del(ID);
            return Json(new { bRet = true, sMsg = "刪除成功" }, "text/html");
        }
        /// <summary>
        /// ajax 加載全部的新聞分類
        /// </summary>
        public void LoadCatalog()
        {
            List<CatalogInfo> lst = (new CatalogCtrl(null)).List();
            Response.Write("{[");
            foreach (CatalogInfo info in lst)
            {
                Response.Write("{ID:'" + info.ID.ToString() + "'");
                Response.Write(",CatalogName:'" + info.CatalogName + "'");
                Response.Write(",PID:'" + info.PID.ToString() + "'");
                Response.Write(",Note:'" + info.Note + "'},");
            }
            Response.Write("]}");
        }
    }
}

這裏面咱們創建的是一個 PartialView ,以便更好的加載到任何頁面中。jquery

我首先簡要介紹一下 Controller 的內容;ajax

public ActionResult NewsCatalogManager(); 完成頁面的加載數據庫

public ActionResult NewCatalog(int PID, string CatalogName, string Note); 支持Ajax實現新分類的添加json

public ActionResult EditCatalog(int ID, string CatalogName, string Note); 支持Ajax對現有分類信息的修改

public ActionResult DeleteCatalog(int ID); 支持Ajax刪除一個分類節點

public void LoadCatalog();支持Ajax提取所有分類信息;

咱們能夠看到除了第一個 NewsCatalogManager()是系統加載使用的外,其他的所有采用Ajax的方式完成數據的管理;

下面咱們看看也沒事如何實現和完成信息調用的吧!

@model IEnumerable<VonPortal.Web.Models.CatalogInfo>
@{
    Layout = null;
}

@Html.Import("header", "zTreeStyle_css", @<link rel="stylesheet" href="/content/zTreeStyle.css" type="text / css">)
@Html.Import("header", "zTreeDemo_css", @<link rel="stylesheet" href="/content/Demo.css" type="text / css">)
@Html.Import("header", "ztree.1.js", @<script type="text/javascript" src="/scripts/jquery.ztree.core.js"></script>)
@Html.Import("header", "ztree.2.js", @<script type="text/javascript" src="/scripts/jquery.ztree.excheck.js"></script>)
@Html.Import("header", "ztree.3.js", @<script type="text/javascript" src="/scripts/jquery.ztree.exedit.js"></script>)

<style type="text/css">
    .ztree li span.button.add {
        margin-left: 2px;
        margin-right: -1px;
        background-position: -144px 0;
        vertical-align: top;
        *vertical-align: middle;
    }
</style>
<div class="content_wrap">
    <ul id="tree" class="ztree" style="width:260px; overflow:auto;"></ul>
</div>
<input id="catalogID" type="hidden" />
<div class="input-group">
    <span class="input-group-addon">分類名稱</span><input id="catalogName" type="text" class="form-control" placeholder="分類名稱" />
</div>
<textarea id="catalogNote" type="text" class="form-control" placeholder="分類說明" ></textarea>
<button type="button" class="btn btn-info" onclick="addNode($('#catalogName').val(), $('#catalogNote').val())">添加同級</button>
<button type="button" class="btn btn-info" onclick="addChild($('#catalogName').val(), $('#catalogNote').val())">添加下級</button>
<button type="button" class="btn btn-info" onclick="editNode($('#catalogName').val(), $('#catalogNote').val())">修改本級</button>

<script type="text/javascript">
    var setting = {
        view: { dblClickExpand: false, showLine: true, selectedMulti: false, selectedMulti: false },
        data: { key: { name: "CatalogName" }, simpleData: { enable: true, idKey: "ID", pIdKey: "PID", rootPId: 0 } },
        edit: {
            enable: true,
            editNameSelectAll: true,
            showRemoveBtn: function (treeId, treeNode) { return !treeNode.isParent; },
            removeTitle: "刪除節點",
            showRenameBtn: true,
            renameTitle: "修改節點"
        },
        callback: {
            beforeRemove: function (treeId, treeNode) {
                var zTree = $.fn.zTree.getZTreeObj("tree");
                zTree.selectNode(treeNode);
                return confirm("確認刪除 節點 -- " + treeNode.name + " 嗎?");
            },
            onRemove: function (e, treeId, treeNode) {
                $.ajax({
                    type: 'POST', url: "News/DeleteCatalog", data: "ID=" + treeNode.ID, dataType: 'json', cache: false,
                    success: function (data) { return true; }
                });
            },
            beforeRename: function (e, treeId, treeNode, isCancel) {
                $.ajax({
                    type: 'POST', url: "News/EditCatalog", data: { "ID": treeId.ID, "CatalogName": treeNode, "Note": treeId.Note }, dataType: 'json', cache: false,
                    success: function (data) { return true; }
                });
            },
            onClick: function (event, treeId, treeNode) {
                var zTree = $.fn.zTree.getZTreeObj("tree");
                zTree.selectNode(treeNode);
                $("#catalogID").val(treeNode.ID);
                $("#catalogName").val(treeNode.CatalogName);
                $("#catalogNote").val(treeNode.Note);
            },
            onDrap:function (event, treeId, treeNodes, targetNode, moveType) {
                alert(treeNodes.length + "," + (targetNode ? (targetNode.tId + ", " + targetNode.name) : "isRoot" ));
            }
        }
    };
    $(function () {
        $.get("News/LoadCatalog", function (response) {
            var data = eval(response);
            $.fn.zTree.init($("#tree"), setting, data);
        });
    });
    function addNode(name, note)
    {
        var zTree = $.fn.zTree.getZTreeObj("tree");
        var nodes = zTree.getSelectedNodes();
        var pid = 0;
        if (nodes.length > 0) pid = nodes[0].PID;
        $.ajax({
            type: 'POST', url: "News/NewCatalog", data: { "PID": pid, "CatalogName": $("#catalogName").val(), "Note": $("#catalogNote").val() }, dataType: 'json', cache: false,
            success: function (data) {
                if (pid > 0) zTree.addNodes(nodes[0].getParentNode(), -1, { ID: data.html, CatalogName: $("#catalogName").val(), PID: pid, Note: $("#catalogNote").val() });
                else zTree.addNodes(null, -1, { ID: data.html, CatalogName: $("#catalogName").val(), PID: pid, Note: $("#catalogNote").val() });
            }
        });
    }
    function addChild(name, note) {
        var zTree = $.fn.zTree.getZTreeObj("tree");
        var nodes = zTree.getSelectedNodes();
        if (nodes.length < 1) alert("還沒有選中節點");
        $.ajax({
            type: 'POST', url: "News/NewCatalog", data: { "PID": nodes[0].ID, "CatalogName": $("#catalogName").val(), "Note": $("#catalogNote").val() }, dataType: 'json', cache: false,
            success: function (data) {
                zTree.addNodes(nodes[0], -1, { ID: data.html, CatalogName: $("#catalogName").val(), PID: nodes[0].ID, Note: $("#catalogNote").val() });
            }
        });
    }
    function editNode(name, note) {
        var zTree = $.fn.zTree.getZTreeObj("tree");
        var nodes = zTree.getSelectedNodes();
        if (nodes.length < 1) alert("還沒有選中節點");
        $.ajax({
            type: 'POST', url: "News/EditCatalog", data: { "ID": nodes[0].ID, "CatalogName": $("#catalogName").val(), "Note": $("#catalogNote").val() }, dataType: 'json', cache: false,
            success: function (data) {
                nodes[0].CatalogName = $("#catalogName").val();
                nodes[0].Note = $("#catalogNote").val();
                zTree.updateNode(nodes[0]);
            }
        });
    }
</script>

首先咱們來分析一下 zTree 的配置信息,也就是setting

    var setting = {
        view: { dblClickExpand: false, showLine: true, selectedMulti: false, selectedMulti: false },
        data: { key: { name: "CatalogName" }, simpleData: { enable: true, idKey: "ID", pIdKey: "PID", rootPId: 0 } },
        edit: {
            enable: true,
            editNameSelectAll: true,
            showRemoveBtn: function (treeId, treeNode) { return !treeNode.isParent; },
            removeTitle: "刪除節點",
            showRenameBtn: true,
            renameTitle: "修改節點"
        },
        callback: {
            beforeRemove: function (treeId, treeNode) {
                var zTree = $.fn.zTree.getZTreeObj("tree");
                zTree.selectNode(treeNode);
                return confirm("確認刪除 節點 -- " + treeNode.name + " 嗎?");
            },
            onRemove: function (e, treeId, treeNode) {
                $.ajax({
                    type: 'POST', url: "News/DeleteCatalog", data: "ID=" + treeNode.ID, dataType: 'json', cache: false,
                    success: function (data) { return true; }
                });
            },
            beforeRename: function (e, treeId, treeNode, isCancel) {
                $.ajax({
                    type: 'POST', url: "News/EditCatalog", data: { "ID": treeId.ID, "CatalogName": treeNode, "Note": treeId.Note }, dataType: 'json', cache: false,
                    success: function (data) { return true; }
                });
            },
            onClick: function (event, treeId, treeNode) {
                var zTree = $.fn.zTree.getZTreeObj("tree");
                zTree.selectNode(treeNode);
                $("#catalogID").val(treeNode.ID);
                $("#catalogName").val(treeNode.CatalogName);
                $("#catalogNote").val(treeNode.Note);
            },
            onDrap:function (event, treeId, treeNodes, targetNode, moveType) {
                alert(treeNodes.length + "," + (targetNode ? (targetNode.tId + ", " + targetNode.name) : "isRoot" ));
            }
        }
    };

重點是 Data 和 callback,在data裏面咱們指定了咱們CatalogInfo的展現方式,也就是說zTree支持咱們自定義數據的展現,咱們在這裏指定了,咱們的展現數據和節點數據的內容:

data: { key: { name: "CatalogName" }, simpleData: { enable: true, idKey: "ID", pIdKey: "PID", rootPId: 0 } },

在callback中咱們定義了全部zTree的操做交互內容:咱們主要定義瞭如下幾個相應事件:

經過這幾個事件,咱們就能夠實現tree的修改和刪除了。

最後爲了完成分類信息的添加和所有信息的修改,咱們創建了一個信息編輯區域:

<input id="catalogID" type="hidden" />
<div class="input-group">
    <span class="input-group-addon">分類名稱</span><input id="catalogName" type="text" class="form-control" placeholder="分類名稱" />
</div>
<textarea id="catalogNote" type="text" class="form-control" placeholder="分類說明" ></textarea>
<button type="button" class="btn btn-info" onclick="addNode($('#catalogName').val(), $('#catalogNote').val())">添加同級</button>
<button type="button" class="btn btn-info" onclick="addChild($('#catalogName').val(), $('#catalogNote').val())">添加下級</button>
<button type="button" class="btn btn-info" onclick="editNode($('#catalogName').val(), $('#catalogNote').val())">修改本級</button>

在這裏面咱們能夠完成信息的同級添加和下級添加,一樣,這是經過ajax完成的,對應的javascript函數是

這樣咱們就完成了zTree的主要基本操做和控制了。

看看我實現的界面效果:

呵呵,還能夠吧!

相關文章
相關標籤/搜索