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的主要基本操做和控制了。
看看我實現的界面效果:
呵呵,還能夠吧!