利用 zTree 在 MVC 下实现树型结构管理

前端之家收集整理的这篇文章主要介绍了利用 zTree 在 MVC 下实现树型结构管理前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

zTree 是一个依靠 jQuery 实现的多功能 “树插件”。优异的性能、灵活的配置、多种功能的组合是 zTree 最大优点。大家可以从zTree 的官网下载和学习。

我这里通过自己的实践,简单介绍一下在.net MVC 环境下,如何实现 ztree 展示和管理树型结构的。

首先我们先假设一下任务,管理新闻的分类,先介绍一下数据结构。

我们还是先建立一个表结构对象和访问控制类

  1. //新闻分类(VON_Catalog)
  2. #region"新闻分类信息类声明"
  3. namespaceVonPortal.Web.Models
  4. {
  5. ///<summary>新闻分类信息类</summary>
  6. publicclassCatalogInfo
  7. {
  8. #region"Constructors"
  9. ///<summary>
  10. ///构造函数
  11. ///</summary>
  12. publicCatalogInfo()
  13. {
  14. }
  15. ///<summary>
  16. ///含初始化构造函数
  17. ///</summary>
  18. ///<paramname="ID">序号</param>
  19. ///<paramname="CatalogName">分类名称</param>
  20. ///<paramname="PID">父类序号</param>
  21. ///<paramname="DisplayOrder">显示序号</param>
  22. ///<paramname="Note">分类说明</param>
  23. publicCatalogInfo(intID,stringCatalogName,intPID,intDisplayOrder,stringNote)
  24. {
  25. this.ID=ID;
  26. this.CatalogName=CatalogName;
  27. this.PID=PID;
  28. this.DisplayOrder=DisplayOrder;
  29. this.Note=Note;
  30. }
  31. #endregion
  32. #region"PublicProperties"
  33. ///<summary>序号</summary>
  34. [required]
  35. [Display(Name="序号")]
  36. publicintID{get;set;}
  37. ///<summary>分类名称</summary>
  38. [Display(Name="分类名称")]
  39. publicstringCatalogName{get;set;}
  40. ///<summary>父类序号</summary>
  41. [required]
  42. [Display(Name="父类序号")]
  43. publicintPID{get;set;}
  44. ///<summary>显示序号</summary>
  45. [Display(Name="显示序号")]
  46. publicintDisplayOrder{get;set;}
  47. ///<summary>分类说明</summary>
  48. [Display(Name="分类说明")]
  49. publicstringNote{get;set;}
  50. #endregion
  51. }
  52. }
  53. #endregion
  54. #region"新闻分类信息基础控制类声明"
  55. namespaceVonPortal.Web.Operators
  56. {
  57. ///<summary>新闻分类控制类</summary>
  58. publicclassCatalogCtrl
  59. {
  60. privateCatalogDataProviderdataProvider=null;
  61. ///<summary>启动数据库事务</summary>
  62. publicIDbTransactionBeginTrans()
  63. {
  64. dataProvider=CatalogDataProvider.CreateProvider();
  65. returndataProvider.DBBeginTrans();
  66. }
  67. ///<summary>含数据库事务的构造函数</summary>
  68. publicCatalogCtrl(IDbTransactionDBTrans)
  69. {
  70. if(DBTrans==null)
  71. dataProvider=CatalogDataProvider.Instance();
  72. else
  73. {
  74. dataProvider=CatalogDataProvider.CreateProvider();
  75. dataProvider.DBTrans=DBTrans;
  76. }
  77. }
  78. //ReaddataandwritetoCatalogInfoclass
  79. privatevoidsetInfoValue(IDataReaderreader,CatalogInfoinfo)
  80. {
  81. info.ID=reader.GetInt32(0);//序号
  82. info.CatalogName=reader.GetString(1);//分类名称
  83. info.PID=reader.GetInt32(2);//父类序号
  84. info.DisplayOrder=reader.GetInt32(3);//显示序号
  85. info.Note=reader.GetString(4);//分类说明
  86. }
  87. ///<summary>检验Catalog信息</summary>
  88. publicstringCheck(CatalogInfoinfo)
  89. {
  90. stringerrInfo="";
  91. returnerrInfo;
  92. }
  93. ///<summary>得到本节点的下属节点新的序号</summary>
  94. publicintNewIdx(intPID)
  95. {
  96. returndataProvider.GetLasterOrder(PID)+1;
  97. }
  98. ///<summary>
  99. ///根据主键PK_Catalog提取信息
  100. ///</summary>
  101. ///<paramname="ID">序号</param>
  102. publicCatalogInfoGetByCatalog(intID)
  103. {
  104. IDataReaderreader=dataProvider.GetByCatalog(ID);
  105. if(!reader.Read())
  106. {
  107. reader.Close();
  108. returnnull;
  109. }
  110. CatalogInfoinfo=newCatalogInfo();
  111. setInfoValue(reader,info);
  112. reader.Close();
  113. returninfo;
  114. }
  115. ///<summary>得到所有信息</summary>
  116. publicList<CatalogInfo>List()
  117. {
  118. List<CatalogInfo>list=newList<CatalogInfo>();
  119. IDataReaderreader=dataProvider.List();
  120. while(reader.Read())
  121. {
  122. CatalogInfoinfo=newCatalogInfo();
  123. setInfoValue(reader,info);
  124. list.Add(info);
  125. }
  126. reader.Close();
  127. returnlist;
  128. }
  129. ///<summary>根据主键IDX_Catalog提取信息</summary>
  130. ///<paramname="PID">父类序号</param>
  131. publicList<CatalogInfo>ListByCatalog(intPID)
  132. {
  133. List<CatalogInfo>list=newList<CatalogInfo>();
  134. IDataReaderreader=dataProvider.ListByCatalog(PID);
  135. while(reader.Read())
  136. {
  137. CatalogInfoinfo=newCatalogInfo();
  138. setInfoValue(reader,info);
  139. list.Add(info);
  140. }
  141. reader.Close();
  142. returnlist;
  143. }
  144. ///<summary>保存Catalog信息</summary>
  145. ///<paramname="info">信息类</param>
  146. publicboolSave(CatalogInfoinfo)
  147. {
  148. info.ID=dataProvider.Save(info.ID,info.CatalogName,info.PID,info.DisplayOrder,info.Note);
  149. returninfo.ID>0;
  150. }
  151. ///<summary>添加Catalog信息</summary>
  152. ///<paramname="info">信息类</param>
  153. publicintAdd(CatalogInfoinfo)
  154. {
  155. info.ID=dataProvider.Add(info.CatalogName,info.Note);
  156. returninfo.ID;
  157. }
  158. ///<summary>修改Catalog信息</summary>
  159. ///<paramname="info">信息类</param>
  160. publicboolEdit(CatalogInfoinfo)
  161. {
  162. returndataProvider.Edit(info.ID,info.Note)>0;
  163. }
  164. ///<summary>根据PK_Catalog删除Catalog信息</summary>
  165. ///<paramname="ID">序号</param>
  166. publicintDel(intID)
  167. {
  168. returndataProvider.Del(ID);
  169. }
  170.  
  171. }
  172. }
  173. #endregion
  174. #region"新闻分类信息操作控制类声明"
  175. namespaceVonPortal.Web.Tasks
  176. {
  177. ///<summary>新闻分类控制类</summary>
  178. publicclassCatalogTask:CatalogCtrl
  179. {
  180. ///<summary>含数据库事务的构造函数</summary>
  181. publicCatalogTask(IDbTransactionDBTrans):base(DBTrans)
  182. {
  183. }
  184. ///<summary>
  185. ///根据主键PK_Catalog提取信息
  186. ///</summary>
  187. ///<paramname="ID">序号</param>
  188. publicnewTask<CatalogInfo>GetByCatalog(intID)
  189. {
  190. returnTask.Run(()=>
  191. {
  192. returnbase.GetByCatalog(ID);
  193. });
  194. }
  195. ///<summary>根据主键IDX_Catalog提取信息</summary>
  196. ///<paramname="PID">父类序号</param>
  197. publicnewTask<List<CatalogInfo>>ListByCatalog(intPID)
  198. {
  199. returnTask.Run(()=>
  200. {
  201. returnbase.ListByCatalog(PID);
  202. });
  203. }
  204. ///<summary>保存Catalog信息</summary>
  205. ///<paramname="info">信息类</param>
  206. publicnewTask<bool>Save(CatalogInfoinfo)
  207. {
  208. returnTask.Run(()=>
  209. {
  210. returnbase.Save(info);
  211. });
  212. }
  213. ///<summary>添加Catalog信息</summary>
  214. ///<paramname="info">信息类</param>
  215. publicnewTask<int>Add(CatalogInfoinfo)
  216. {
  217. returnTask.Run(()=>
  218. {
  219. returnbase.Add(info);
  220. });
  221. }
  222. ///<summary>修改Catalog信息</summary>
  223. ///<paramname="info">信息类</param>
  224. publicnewTask<bool>Edit(CatalogInfoinfo)
  225. {
  226. returnTask.Run(()=>
  227. {
  228. returnbase.Edit(info);
  229. });
  230. }
  231. ///<summary>根据PK_Catalog删除Catalog信息</summary>
  232. ///<paramname="ID">序号</param>
  233. publicnewTask<int>Del(intID)
  234. {
  235. returnTask.Run(()=>
  236. {
  237. returnbase.Del(ID);
  238. });
  239. }
  240.  
  241. }
  242. }
  243. #endregion
  244. #region"新闻分类信息数据库访问基类声明"
  245. namespaceVonPortal.Web.Data
  246. {
  247. ///<summary>
  248. ///数据及操作控制层
  249. ///<seealsocref="VonPortal.Web.Business.CatalogInfo"/>
  250. ///<seealsocref="VonPortal.Web.Business.CatalogCtrl"/>
  251. ///</summary>
  252. publicabstractclassCatalogDataProvider:DataProvider
  253. {
  254. #regionShared/StaticMethods
  255. //singletonreferencetotheinstantiatedobject
  256. privatestaticCatalogDataProviderobjProvider=null;
  257. ///<summary>
  258. ///constructor
  259. ///</summary>
  260. staticCatalogDataProvider()
  261. {
  262. objProvider=CreateProvider();
  263. }
  264. ///<summary>
  265. ///dynamicallycreateprovider
  266. ///</summary>
  267. ///<returns>returntheprovider</returns>
  268. publicstaticCatalogDataProviderCreateProvider()
  269. {
  270. return(CatalogDataProvider)VonPortal.Web.Reflection.CreateDataProvider("von","NewsModule","VonPortal.Web.Data.CatalogDataProvider");
  271. }
  272. ///<summary>
  273. ///TheinstanceofCatalogDataProvider.
  274. ///</summary>
  275. ///<returns>returntheprovider</returns>
  276. publicstaticCatalogDataProviderInstance()
  277. {
  278. if(objProvider==null)objProvider=CreateProvider();
  279. returnobjProvider;
  280. }
  281. #endregion
  282.  
  283. #region"CatalogAbstractMethods"
  284. ///<summary>根据主键PK_Catalog提取信息</summary>
  285. publicabstractIDataReaderGetByCatalog(intID);
  286. ///<summary>得到本节点的最后节点序号</summary>
  287. publicabstractintGetLasterOrder(intPID);
  288. ///<summary>根据主键IDX_Catalog提取信息</summary>
  289. publicabstractIDataReaderListByCatalog(intPID);
  290. ///<summary>提取全部信息</summary>
  291. publicabstractIDataReaderList();
  292. ///<summary>保存Catalog信息</summary>
  293. publicabstractintSave(intID,stringNote);
  294. ///<summary>添加Catalog信息</summary>
  295. publicabstractintAdd(stringCatalogName,stringNote);
  296. ///<summary>修改Catalog信息</summary>
  297. publicabstractintEdit(intID,stringNote);
  298. ///<summary>根据PK_Catalog删除Catalog信息</summary>
  299. publicabstractintDel(intID);
  300. #endregion
  301. }
  302. }
  303. #endregion

最后的VonPortal.Web.Data.CatalogDataProvider是数据库访问接口类,您可以根据自己的实际需求建立真实的数据库访问类,完成真正的数据库访问,我这里就不在详述了,下面将介绍今天的主角 zTree。大家可以通过 zTree 网上 API 来深入了解其代码编写规则,也可以下载Demo来进行研究。

首先按照常规我们先建立后台处理的Model和Controller。

  1. usingSystem.ComponentModel.DataAnnotations;
  2. //新闻分类(VON_Catalog)
  3. namespaceVonPortal.Web.Modules
  4. {
  5. publicclassCatalogModel
  6. {
  7.  
  8. ///<summary>序号</summary>
  9. [required]
  10. [Display(Name="序号")]
  11. publicintID{get;set;}
  12. ///<summary>分类名称</summary>
  13. [Display(Name="分类名称")]
  14. publicstringCatalogName{get;set;}
  15. ///<summary>父类序号</summary>
  16. [required]
  17. [Display(Name="父类序号")]
  18. publicintPID{get;set;}
  19. ///<summary>显示序号</summary>
  20. [Display(Name="显示序号")]
  21. publicintDisplayOrder{get;set;}
  22. ///<summary>分类说明</summary>
  23. [Display(Name="分类说明")]
  24. publicstringNote{get;set;}
  25. }
  26. }
  1. usingSystem.Collections.Generic;
  2. usingSystem.Web.Mvc;
  3. usingVonPortal.Web.Models;
  4. usingVonPortal.Web.Operators;
  5.  
  6. namespaceVonPortal.Web.Modules.Controllers
  7. {
  8. publicclassNewsController:Controller
  9. {
  10. publicActionResultNewsCatalogManager()
  11. {
  12. returnPartialView();
  13. }
  14. ///<summary>
  15. ///ajax添加一个新闻分类
  16. ///</summary>
  17. ///<paramname="PID">上级序号</param>
  18. ///<paramname="CatalogName">分类名称</param>
  19. ///<paramname="Note">分类说明</param>
  20. ///<returns></returns>
  21. [HttpPost]
  22. publicActionResultNewCatalog(intPID,stringNote)
  23. {
  24. CatalogCtrlctrl=newCatalogCtrl(null);
  25. CatalogInfoinfo=newCatalogInfo(0,CatalogName,PID,Note);
  26. info.DisplayOrder=ctrl.NewIdx(PID);
  27. ctrl.Add(info);
  28. returnJson(new{bRet=true,sMsg="添加成功",html=info.ID.ToString()},"text/html");
  29. }
  30. ///<summary>
  31. ///ajax修改一个分类信息
  32. ///</summary>
  33. ///<paramname="PID">上级序号</param>
  34. ///<paramname="CatalogName">分类名称</param>
  35. ///<paramname="Note">分类说明</param>
  36. ///<returns></returns>
  37. [HttpPost]
  38. publicActionResultEditCatalog(intID,stringNote)
  39. {
  40. CatalogCtrlctrl=newCatalogCtrl(null);
  41. CatalogInfoinfo=ctrl.GetByCatalog(ID);
  42. info.CatalogName=CatalogName;
  43. info.Note=Note;
  44. ctrl.Edit(info);
  45. returnJson(new{bRet=true,sMsg="修改成功"},"text/html");
  46. }
  47. ///<summary>
  48. ///ajax删除一个新闻分类
  49. ///</summary>
  50. ///<paramname="ID">分类序号</param>
  51. ///<returns></returns>
  52. [HttpPost]
  53. publicActionResultDeleteCatalog(intID)
  54. {
  55. CatalogCtrlctrl=newCatalogCtrl(null);
  56. ctrl.Del(ID);
  57. returnJson(new{bRet=true,sMsg="删除成功"},"text/html");
  58. }
  59. ///<summary>
  60. ///ajax加载所有的新闻分类
  61. ///</summary>
  62. publicvoidLoadCatalog()
  63. {
  64. List<CatalogInfo>lst=(newCatalogCtrl(null)).List();
  65. Response.Write("{[");
  66. foreach(CatalogInfoinfoinlst)
  67. {
  68. Response.Write("{ID:'"+info.ID.ToString()+"'");
  69. Response.Write(",CatalogName:'"+info.CatalogName+"'");
  70. Response.Write(",PID:'"+info.PID.ToString()+"'");
  71. Response.Write(",Note:'"+info.Note+"'},");
  72. }
  73. Response.Write("]}");
  74. }
  75. }
  76. }

这里面我们建立的是一个PartialView ,以便更好的加载到任何页面中。

我首先简要介绍一下 Controller 的内容

public ActionResult NewsCatalogManager(); 完成页面的加载

public ActionResult NewCatalog(int PID,string CatalogName,string Note); 支持Ajax实现新分类添加

public ActionResult EditCatalog(int ID,string Note); 支持Ajax对现有分类信息的修改

public ActionResult DeleteCatalog(int ID); 支持Ajax删除一个分类节点

public void LoadCatalog();支持Ajax提取全部分类信息;

我们可以看到除了第一个NewsCatalogManager()是系统加载使用的外,其余的全部采用Ajax的方式完成数据的管理;

下面我们看看也没事如何实现和完成信息调用的吧!

  1. @modelIEnumerable<VonPortal.Web.Models.CatalogInfo>
  2. @{
  3. Layout=null;
  4. }
  5.  
  6. @Html.Import("header","zTreeStyle_css",@<linkrel="stylesheet"href="/content/zTreeStyle.css"type="text/css">)
  7. @Html.Import("header","zTreeDemo_css",@<linkrel="stylesheet"href="/content/Demo.css"type="text/css">)
  8. @Html.Import("header","ztree.1.js",@<scripttype="text/javascript"src="/scripts/jquery.ztree.core.js"></script>)
  9. @Html.Import("header","ztree.2.js",@<scripttype="text/javascript"src="/scripts/jquery.ztree.excheck.js"></script>)
  10. @Html.Import("header","ztree.3.js",@<scripttype="text/javascript"src="/scripts/jquery.ztree.exedit.js"></script>)
  11.  
  12. <styletype="text/css">
  13. .ztreelispan.button.add{
  14. margin-left:2px;
  15. margin-right:-1px;
  16. background-position:-144px0;
  17. vertical-align:top;
  18. *vertical-align:middle;
  19. }
  20. </style>
  21. <divclass="content_wrap">
  22. <ulid="tree"class="ztree"style="width:260px;overflow:auto;"></ul>
  23. </div>
  24. <inputid="catalogID"type="hidden"/>
  25. <divclass="input-group">
  26. <spanclass="input-group-addon">分类名称</span><inputid="catalogName"type="text"class="form-control"placeholder="分类名称"/>
  27. </div>
  28. <textareaid="catalogNote"type="text"class="form-control"placeholder="分类说明"></textarea>
  29. <buttontype="button"class="btnbtn-info"onclick="addNode($('#catalogName').val(),$('#catalogNote').val())">添加同级</button>
  30. <buttontype="button"class="btnbtn-info"onclick="addChild($('#catalogName').val(),$('#catalogNote').val())">添加下级</button>
  31. <buttontype="button"class="btnbtn-info"onclick="editNode($('#catalogName').val(),$('#catalogNote').val())">修改本级</button>
  32.  
  33. <scripttype="text/javascript">
  34. varsetting={
  35. view:{dblClickExpand:false,showLine:true,selectedMulti:false,selectedMulti:false},data:{key:{name:"CatalogName"},simpleData:{enable:true,idKey:"ID",pIdKey:"PID",rootPId:0}},edit:{
  36. enable:true,editNameSelectAll:true,showRemoveBtn:function(treeId,treeNode){return!treeNode.isParent;},removeTitle:"删除节点",showRenameBtn:true,renameTitle:"修改节点"
  37. },callback:{
  38. beforeRemove:function(treeId,treeNode){
  39. varzTree=$.fn.zTree.getZTreeObj("tree");
  40. zTree.selectNode(treeNode);
  41. returnconfirm("确认删除节点--"+treeNode.name+"吗?");
  42. },onRemove:function(e,treeId,treeNode){
  43. $.ajax({
  44. type:'POST',url:"News/DeleteCatalog",data:"ID="+treeNode.ID,dataType:'json',cache:false,success:function(data){returntrue;}
  45. });
  46. },beforeRename:function(e,treeNode,isCancel){
  47. $.ajax({
  48. type:'POST',url:"News/EditCatalog",data:{"ID":treeId.ID,"CatalogName":treeNode,"Note":treeId.Note},onClick:function(event,treeNode){
  49. varzTree=$.fn.zTree.getZTreeObj("tree");
  50. zTree.selectNode(treeNode);
  51. $("#catalogID").val(treeNode.ID);
  52. $("#catalogName").val(treeNode.CatalogName);
  53. $("#catalogNote").val(treeNode.Note);
  54. },onDrap:function(event,treeNodes,targetNode,moveType){
  55. alert(treeNodes.length+","+(targetNode?(targetNode.tId+","+targetNode.name):"isRoot"));
  56. }
  57. }
  58. };
  59. $(function(){
  60. $.get("News/LoadCatalog",function(response){
  61. vardata=eval(response);
  62. $.fn.zTree.init($("#tree"),setting,data);
  63. });
  64. });
  65. functionaddNode(name,note)
  66. {
  67. varzTree=$.fn.zTree.getZTreeObj("tree");
  68. varnodes=zTree.getSelectedNodes();
  69. varpid=0;
  70. if(nodes.length>0)pid=nodes[0].PID;
  71. $.ajax({
  72. type:'POST',url:"News/NewCatalog",data:{"PID":pid,"CatalogName":$("#catalogName").val(),"Note":$("#catalogNote").val()},success:function(data){
  73. if(pid>0)zTree.addNodes(nodes[0].getParentNode(),-1,{ID:data.html,CatalogName:$("#catalogName").val(),PID:pid,Note:$("#catalogNote").val()});
  74. elsezTree.addNodes(null,Note:$("#catalogNote").val()});
  75. }
  76. });
  77. }
  78. functionaddChild(name,note){
  79. varzTree=$.fn.zTree.getZTreeObj("tree");
  80. varnodes=zTree.getSelectedNodes();
  81. if(nodes.length<1)alert("尚未选中节点");
  82. $.ajax({
  83. type:'POST',data:{"PID":nodes[0].ID,success:function(data){
  84. zTree.addNodes(nodes[0],PID:nodes[0].ID,Note:$("#catalogNote").val()});
  85. }
  86. });
  87. }
  88. functioneditNode(name,data:{"ID":nodes[0].ID,success:function(data){
  89. nodes[0].CatalogName=$("#catalogName").val();
  90. nodes[0].Note=$("#catalogNote").val();
  91. zTree.updateNode(nodes[0]);
  92. }
  93. });
  94. }
  95. </script>

首先我们来分析一下 zTree 的配置信息,也就是setting

  1. varsetting={
  2. view:{dblClickExpand:false,"+targetNode.name):"isRoot"));
  3. }
  4. }
  5. };

重点是 Data 和 callback,在data里面我们指定了我们CatalogInfo的展示方式,也就是说zTree支持我们自定义数据的展示,我们在这里指定了,我们的展示数据和节点数据的内容

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

在callback中我们定义了所有zTree的操作交互内容:我们主要定义了以下几个相应事件:

通过这几个事件,我们就可以实现tree的修改删除了。

最后为了完成分类信息的添加和全部信息的修改,我们建立了一个信息编辑区域:

  1. <inputid="catalogID"type="hidden"/>
  2. <divclass="input-group">
  3. <spanclass="input-group-addon">分类名称</span><inputid="catalogName"type="text"class="form-control"placeholder="分类名称"/>
  4. </div>
  5. <textareaid="catalogNote"type="text"class="form-control"placeholder="分类说明"></textarea>
  6. <buttontype="button"class="btnbtn-info"onclick="addNode($('#catalogName').val(),$('#catalogNote').val())">修改本级</button>

在这里面我们可以完成信息的同级添加和下级添加,同样,这是通过ajax完成的,对应的javascript函数

这样我们就完成了zTree的主要基本操作和控制了。

看看我实现的界面效果

呵呵,还可以吧!

猜你在找的Ajax相关文章