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

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

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

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

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

//新闻分类(VON_Catalog)
#region"新闻分类信息类声明"
namespaceVonPortal.Web.Models
{
///<summary>新闻分类信息类</summary>
publicclassCatalogInfo
{
#region"Constructors"
///<summary>
///构造函数
///</summary>
publicCatalogInfo()
{
}
///<summary>
///含初始化构造函数
///</summary>
///<paramname="ID">序号</param>
///<paramname="CatalogName">分类名称</param>
///<paramname="PID">父类序号</param>
///<paramname="displayOrder">显示序号</param>
///<paramname="Note">分类说明</param>
publicCatalogInfo(intID,stringCatalogName,intPID,intdisplayOrder,stringNote)
{
this.ID=ID;
this.CatalogName=CatalogName;
this.PID=PID;
this.displayOrder=displayOrder;
this.Note=Note;
}
#endregion
#region"PublicProperties"
///<summary>序号</summary>
[required]
[display(Name="序号")]
publicintID{get;set;}
///<summary>分类名称</summary>
[display(Name="分类名称")]
publicstringCatalogName{get;set;}
///<summary>父类序号</summary>
[required]
[display(Name="父类序号")]
publicintPID{get;set;}
///<summary>显示序号</summary>
[display(Name="显示序号")]
publicintdisplayOrder{get;set;}
///<summary>分类说明</summary>
[display(Name="分类说明")]
publicstringNote{get;set;}
#endregion
}
}
#endregion
#region"新闻分类信息基础控制类声明"
namespaceVonPortal.Web.Operators
{
///<summary>新闻分类控制类</summary>
publicclassCatalogCtrl
{
privateCatalogDataProviderdataProvider=null;
///<summary>启动数据库事务</summary>
publicIDbTransactionBeginTrans()
{
dataProvider=CatalogDataProvider.CreateProvider();
returndataProvider.DBBeginTrans();
}
///<summary>含数据库事务的构造函数</summary>
publicCatalogCtrl(IDbTransactionDBTrans)
{
if(DBTrans==null)
dataProvider=CatalogDataProvider.Instance();
else
{
dataProvider=CatalogDataProvider.CreateProvider();
dataProvider.DBTrans=DBTrans;
}
}
//ReaddataandwritetoCatalogInfoclass
privatevoidsetInfovalue(IDataReaderreader,CatalogInfoinfo)
{
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>
publicstringCheck(CatalogInfoinfo)
{
stringerrInfo="";
returnerrInfo;
}
///<summary>得到本节点的下属节点新的序号</summary>
publicintNewIdx(intPID)
{
returndataProvider.GetLasterOrder(PID)+1;
}
///<summary>
///根据主键PK_Catalog提取信息
///</summary>
///<paramname="ID">序号</param>
publicCatalogInfoGetByCatalog(intID)
{
IDataReaderreader=dataProvider.GetByCatalog(ID);
if(!reader.Read())
{
reader.Close();
returnnull;
}
CatalogInfoinfo=newCatalogInfo();
setInfovalue(reader,info);
reader.Close();
returninfo;
}
///<summary>得到所有信息</summary>
publicList<CatalogInfo>List()
{
List<CatalogInfo>list=newList<CatalogInfo>();
IDataReaderreader=dataProvider.List();
while(reader.Read())
{
CatalogInfoinfo=newCatalogInfo();
setInfovalue(reader,info);
list.Add(info);
}
reader.Close();
returnlist;
}
///<summary>根据主键IDX_Catalog提取信息</summary>
///<paramname="PID">父类序号</param>
publicList<CatalogInfo>ListByCatalog(intPID)
{
List<CatalogInfo>list=newList<CatalogInfo>();
IDataReaderreader=dataProvider.ListByCatalog(PID);
while(reader.Read())
{
CatalogInfoinfo=newCatalogInfo();
setInfovalue(reader,info);
list.Add(info);
}
reader.Close();
returnlist;
}
///<summary>保存Catalog信息</summary>
///<paramname="info">信息类</param>
publicboolSave(CatalogInfoinfo)
{
info.ID=dataProvider.Save(info.ID,info.CatalogName,info.PID,info.displayOrder,info.Note);
returninfo.ID>0;
}
///<summary>添加Catalog信息</summary>
///<paramname="info">信息类</param>
publicintAdd(CatalogInfoinfo)
{
info.ID=dataProvider.Add(info.CatalogName,info.Note);
returninfo.ID;
}
///<summary>修改Catalog信息</summary>
///<paramname="info">信息类</param>
publicboolEdit(CatalogInfoinfo)
{
returndataProvider.Edit(info.ID,info.Note)>0;
}
///<summary>根据PK_Catalog删除Catalog信息</summary>
///<paramname="ID">序号</param>
publicintDel(intID)
{
returndataProvider.Del(ID);
}

}
}
#endregion
#region"新闻分类信息操作控制类声明"
namespaceVonPortal.Web.Tasks
{
///<summary>新闻分类控制类</summary>
publicclassCatalogTask:CatalogCtrl
{
///<summary>含数据库事务的构造函数</summary>
publicCatalogTask(IDbTransactionDBTrans):base(DBTrans)
{
}
///<summary>
///根据主键PK_Catalog提取信息
///</summary>
///<paramname="ID">序号</param>
publicnewTask<CatalogInfo>GetByCatalog(intID)
{
returnTask.Run(()=>
{
returnbase.GetByCatalog(ID);
});
}
///<summary>根据主键IDX_Catalog提取信息</summary>
///<paramname="PID">父类序号</param>
publicnewTask<List<CatalogInfo>>ListByCatalog(intPID)
{
returnTask.Run(()=>
{
returnbase.ListByCatalog(PID);
});
}
///<summary>保存Catalog信息</summary>
///<paramname="info">信息类</param>
publicnewTask<bool>Save(CatalogInfoinfo)
{
returnTask.Run(()=>
{
returnbase.Save(info);
});
}
///<summary>添加Catalog信息</summary>
///<paramname="info">信息类</param>
publicnewTask<int>Add(CatalogInfoinfo)
{
returnTask.Run(()=>
{
returnbase.Add(info);
});
}
///<summary>修改Catalog信息</summary>
///<paramname="info">信息类</param>
publicnewTask<bool>Edit(CatalogInfoinfo)
{
returnTask.Run(()=>
{
returnbase.Edit(info);
});
}
///<summary>根据PK_Catalog删除Catalog信息</summary>
///<paramname="ID">序号</param>
publicnewTask<int>Del(intID)
{
returnTask.Run(()=>
{
returnbase.Del(ID);
});
}

}
}
#endregion
#region"新闻分类信息数据库访问基类声明"
namespaceVonPortal.Web.Data
{
///<summary>
///数据及操作控制层
///<seealsocref="VonPortal.Web.Business.CatalogInfo"/>
///<seealsocref="VonPortal.Web.Business.CatalogCtrl"/>
///</summary>
publicabstractclassCatalogDataProvider:DataProvider
{
#regionShared/Staticmethods
//singletonreferencetotheinstantiatedobject
privatestaticCatalogDataProviderobjProvider=null;
///<summary>
///constructor
///</summary>
staticCatalogDataProvider()
{
objProvider=CreateProvider();
}
///<summary>
///dynamicallycreateprovider
///</summary>
///<returns>returntheprovider</returns>
publicstaticCatalogDataProviderCreateProvider()
{
return(CatalogDataProvider)VonPortal.Web.Reflection.CreateDataProvider("von","NewsModule","VonPortal.Web.Data.CatalogDataProvider");
}
///<summary>
///TheinstanceofCatalogDataProvider.
///</summary>
///<returns>returntheprovider</returns>
publicstaticCatalogDataProviderInstance()
{
if(objProvider==null)objProvider=CreateProvider();
returnobjProvider;
}
#endregion

#region"CatalogAbstractMethods"
///<summary>根据主键PK_Catalog提取信息</summary>
publicabstractIDataReaderGetByCatalog(intID);
///<summary>得到本节点的最后节点序号</summary>
publicabstractintGetLasterOrder(intPID);
///<summary>根据主键IDX_Catalog提取信息</summary>
publicabstractIDataReaderListByCatalog(intPID);
///<summary>提取全部信息</summary>
publicabstractIDataReaderList();
///<summary>保存Catalog信息</summary>
publicabstractintSave(intID,stringNote);
///<summary>添加Catalog信息</summary>
publicabstractintAdd(stringCatalogName,stringNote);
///<summary>修改Catalog信息</summary>
publicabstractintEdit(intID,stringNote);
///<summary>根据PK_Catalog删除Catalog信息</summary>
publicabstractintDel(intID);
#endregion
}
}
#endregion

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

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

usingSystem.ComponentModel.DataAnnotations;
//新闻分类(VON_Catalog)
namespaceVonPortal.Web.Modules
{
publicclassCatalogModel
{

///<summary>序号</summary>
[required]
[display(Name="序号")]
publicintID{get;set;}
///<summary>分类名称</summary>
[display(Name="分类名称")]
publicstringCatalogName{get;set;}
///<summary>父类序号</summary>
[required]
[display(Name="父类序号")]
publicintPID{get;set;}
///<summary>显示序号</summary>
[display(Name="显示序号")]
publicintdisplayOrder{get;set;}
///<summary>分类说明</summary>
[display(Name="分类说明")]
publicstringNote{get;set;}
}
}
usingSystem.Collections.Generic;
usingSystem.Web.Mvc;
usingVonPortal.Web.Models;
usingVonPortal.Web.Operators;

namespaceVonPortal.Web.Modules.Controllers
{
publicclassNewsController:Controller
{
publicActionResultNewscatalogManager()
{
returnPartialView();
}
///<summary>
///ajax添加一个新闻分类
///</summary>
///<paramname="PID">上级序号</param>
///<paramname="CatalogName">分类名称</param>
///<paramname="Note">分类说明</param>
///<returns></returns>
[HttpPost]
publicActionResultNewCatalog(intPID,stringNote)
{
CatalogCtrlctrl=newCatalogCtrl(null);
CatalogInfoinfo=newCatalogInfo(0,CatalogName,PID,Note);
info.displayOrder=ctrl.NewIdx(PID);
ctrl.Add(info);
returnjson(new{bRet=true,sMsg="添加成功",html=info.ID.ToString()},"text/html");
}
///<summary>
///ajax修改一个分类信息
///</summary>
///<paramname="PID">上级序号</param>
///<paramname="CatalogName">分类名称</param>
///<paramname="Note">分类说明</param>
///<returns></returns>
[HttpPost]
publicActionResultEditCatalog(intID,stringNote)
{
CatalogCtrlctrl=newCatalogCtrl(null);
CatalogInfoinfo=ctrl.GetByCatalog(ID);
info.CatalogName=CatalogName;
info.Note=Note;
ctrl.Edit(info);
returnjson(new{bRet=true,sMsg="修改成功"},"text/html");
}
///<summary>
///ajax删除一个新闻分类
///</summary>
///<paramname="ID">分类序号</param>
///<returns></returns>
[HttpPost]
publicActionResultDeleteCatalog(intID)
{
CatalogCtrlctrl=newCatalogCtrl(null);
ctrl.Del(ID);
returnjson(new{bRet=true,sMsg="删除成功"},"text/html");
}
///<summary>
///ajax加载所有的新闻分类
///</summary>
publicvoidLoadCatalog()
{
List<CatalogInfo>lst=(newCatalogCtrl(null)).List();
Response.Write("{[");
foreach(CatalogInfoinfoinlst)
{
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 ,以便更好的加载到任何页面中。

我首先简要介绍一下 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的方式完成数据的管理;

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

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

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

<styletype="text/css">
.ztreelispan.button.add{
margin-left:2px;
margin-right:-1px;
background-position:-144px0;
vertical-align:top;
*vertical-align:middle;
}
</style>
<divclass="content_wrap">
<ulid="tree"class="ztree"style="width:260px;overflow:auto;"></ul>
</div>
<inputid="catalogID"type="hidden"/>
<divclass="input-group">
<spanclass="input-group-addon">分类名称</span><inputid="catalogName"type="text"class="form-control"placeholder="分类名称"/>
</div>
<textareaid="catalogNote"type="text"class="form-control"placeholder="分类说明"></textarea>
<buttontype="button"class="btnbtn-info"onclick="addNode($('#catalogName').val(),$('#catalogNote').val())">添加同级</button>
<buttontype="button"class="btnbtn-info"onclick="addChild($('#catalogName').val(),$('#catalogNote').val())">添加下级</button>
<buttontype="button"class="btnbtn-info"onclick="editNode($('#catalogName').val(),$('#catalogNote').val())">修改本级</button>

<scripttype="text/javascript">
varsetting={
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){
varzTree=$.fn.zTree.getZTreeObj("tree");
zTree.selectNode(treeNode);
returnconfirm("确认删除节点--"+treeNode.name+"吗?");
},onRemove:function(e,treeId,treeNode){
$.ajax({
type:'POST',url:"News/DeleteCatalog",data:"ID="+treeNode.ID,dataType:'json',cache:false,success:function(data){returntrue;}
});
},beforeRename:function(e,treeNode,isCancel){
$.ajax({
type:'POST',url:"News/EditCatalog",data:{"ID":treeId.ID,"CatalogName":treeNode,"Note":treeId.Note},onClick:function(event,treeNode){
varzTree=$.fn.zTree.getZTreeObj("tree");
zTree.selectNode(treeNode);
$("#catalogID").val(treeNode.ID);
$("#catalogName").val(treeNode.CatalogName);
$("#catalogNote").val(treeNode.Note);
},onDrap:function(event,treeNodes,targetNode,moveType){
alert(treeNodes.length+","+(targetNode?(targetNode.tId+","+targetNode.name):"isRoot"));
}
}
};
$(function(){
$.get("News/LoadCatalog",function(response){
vardata=eval(response);
$.fn.zTree.init($("#tree"),setting,data);
});
});
functionaddNode(name,note)
{
varzTree=$.fn.zTree.getZTreeObj("tree");
varnodes=zTree.getSelectednodes();
varpid=0;
if(nodes.length>0)pid=nodes[0].PID;
$.ajax({
type:'POST',url:"News/NewCatalog",data:{"PID":pid,"CatalogName":$("#catalogName").val(),"Note":$("#catalogNote").val()},success:function(data){
if(pid>0)zTree.addNodes(nodes[0].getParentNode(),-1,{ID:data.html,CatalogName:$("#catalogName").val(),PID:pid,Note:$("#catalogNote").val()});
elsezTree.addNodes(null,Note:$("#catalogNote").val()});
}
});
}
functionaddChild(name,note){
varzTree=$.fn.zTree.getZTreeObj("tree");
varnodes=zTree.getSelectednodes();
if(nodes.length<1)alert("尚未选中节点");
$.ajax({
type:'POST',data:{"PID":nodes[0].ID,success:function(data){
zTree.addNodes(nodes[0],PID:nodes[0].ID,Note:$("#catalogNote").val()});
}
});
}
functioneditNode(name,data:{"ID":nodes[0].ID,success:function(data){
nodes[0].CatalogName=$("#catalogName").val();
nodes[0].Note=$("#catalogNote").val();
zTree.updateNode(nodes[0]);
}
});
}
</script>

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

varsetting={
view:{dblClickExpand:false,"+targetNode.name):"isRoot"));
}
}
};

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

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

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

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

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

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

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

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

看看我实现的界面效果

呵呵,还可以吧!

相关文章

IE6是一个非常老旧的网页浏览器,虽然现在很少人再使用它,但...
PHP中的count()函数是用来计算数组或容器中元素的个数。这个...
使用 AJAX(Asynchronous JavaScript and XML)技术可以在不...
Ajax(Asynchronous JavaScript and XML)是一种用于改进网页...
本文将介绍如何通过AJAX下载Excel文件流。通过AJAX,我们可以...
Ajax是一种用于客户端和服务器之间的异步通信技术。通过Ajax...