基于asp.net ajax 的异步加载无限级联树状菜单
最近一个项目用到这个功能,本来以为很容易,但写起来发现还是有点小难度的,现在写出来分享,如果不好不要拍砖~
效果图 1
效果图 2
效果图 3
一:数据库设计
数据字典:
menuID:自增列
menuName:菜单名
url:菜单指向
parentID:父级菜单ID,根目录为 0
menuIndex:菜单排序ID
menuLevel:菜单级别,根级别为 0
menuImg:菜单图片
二:数据实体类
需要这个类是为了前台操作的方便,这里要感慨微软的ajax框架确实功能强大,通过它,在前台用Javascript可以很方便的操作。这个类对应于数据表,非常简单。
/// 菜单信息类
/// </summary>
[Serializable]
public class MenuInfo
{
public int MenuId { get; set; }
public string MenuName { get; set; }
public string Url { get; set; }
public int ParentId { get; set; }
public int MenuIndex { get; set; }
public int MenuLevel { get; set; }
public int ChildCount { get; set; }
public MenuInfo() { }
}
如果是VS2008,可以不用加 [Serializable] 特性。
三:数据库操作类
因为是异步加载,点击菜单项时才会加载下一级的菜单,所以sql 语句根据传入的parentID来检索数据。
/// 根据parentId 分级获取菜单,用于Ajax加载模式
/// </summary>
/// <param name="parentId"></param>
/// <returns></returns>
public List<MenuInfo> GetChildMenus(int parentId)
{
string commandText = @"
select
[menuId],
[menuName],
[url],
[parentId],
[MenuIndex],
[MenuLevel],
(select count(0) from b_Menu f where f.parentid= m.menuId)as ChildCount
from
b_Menu m
where
parentid=@parentId ORDER BY MenuIndex";
DbParameter[] parms =
{
DBSQLHelper.MakeInParam("@parentId",(DbType)SqlDbType.Int,parentId)
};
return LoadMenuInfo(DBSQLHelper.ExecuteReader(CommandType.Text, commandText, parms));
}
{
List<MenuInfo> menuList = new List<MenuInfo>();
while (dr.Read())
{
MenuInfo m = new MenuInfo();
m.MenuId = TypeParse.ObjectToInt(dr["menuid"]);
m.MenuName = dr["menuName"].ToString().Trim();
m.ParentId = TypeParse.ObjectToInt(dr["parentId"]);
m.Url = dr["url"].ToString();
if (dr.GetSchemaTable().Rows[6][0].ToString() ==("ChildCount"))
{
m.ChildCount = TypeParse.ObjectToInt(dr["ChildCount"]);
}
m.MenuIndex = TypeParse.ObjectToInt(dr["MenuIndex"]);
m.MenuLevel = TypeParse.ObjectToInt(dr["MenuLevel"]);
menuList.Add(m);
}
dr.Close();
return menuList;
}
四:使用WebService调用数据类
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class BGMService : System.Web.Services.WebService {
public BGMService() {
}
}
public List<MenuInfo> GetChildMenus(string parentId)
{
//System.Threading.Thread.Sleep(1000);
return Menu.GetChildMenu(parentId);
}
五:JavaScript接受数据
前面已经说了,asp.net ajax 对javascript调用webservice有很好的支持,具体怎么做呢?
1.将 ScriptManager 控件拖到页面上。
2.设置webservice引用地址
<asp:ServiceReference Path="~/BackManageMent/webservice/BGMService.asmx" />
</Services>
然后在javascript 中就可以用 类名.方法名() 调用了。在本列中就是 BGMService.GetChildMenus() 。简单吧。
接到数据之后的处理比较麻烦,需要一定的javascript功底。我先把代码贴出来,有时间在加注释。
div #treeMenus img{cursor:pointer;}
div #treeMenus .Level_1
{
margin:0;
padding:0;
}
div #treeMenus .Level_2
{
margin:0;
padding:0;
}
div #treeMenus .machn
{
margin:0;
margin-left:40px;
padding:0;
}
div #treeMenus ul
{
list-style-type:none;
width:100%;
height:auto;
clear:both;
}
div #treeMenus ul li
{
list-style-image:none;
float:left;
margin-right:4px;
}
div #treeMenus ul .li_txt
{
padding-top:4px;
cursor:pointer;
}
div #treeMenus ul .li_txt:hover
{
text-decoration:underline;
}
{
var _self=this;
BGMService.GetChildMenus("0",function(value){onGetTree(value,frame.treeMenus)},onFailure);//在asp.net Ajax的回调函数中传递自定义参数的方法
var nodeImgs=
{
nodeOpened:"images/treeImg/nodeOpened.gif",
nodeOpener:"images/treeImg/NodeOpener.gif",
nodeOpening:"images/treeImg/nodeOpening.gif",
nodeNoChd:"images/treeImg/ext.axd.gif",
fopen:"images/treeImg/fopen.gif",
fclose:"images/treeImg/fclose.gif",
file:"images/treeImg/l4.gif",
nodeTopOpen:"images/treeImg/node_top_1.gif",
nodeTopClose:"images/treeImg/node_top.gif",
nodeMidOpen:"images/treeImg/mnode.gif",
nodeMidClose:"images/treeImg/pnode.gif",
nodeBotOpen:"images/treeImg/plastnode_1.gif",
nodeBotClose:"images/treeImg/plastnode.gif",
nodeVertLine:'images/treeImg/vertline.gif',
nodeThreeLine:'images/treeImg/node.gif',
nodeBlank:'images/treeImg/blank.gif',
nodeLastLine:'images/treeImg/lastnode.gif',
nodeLoading:'images/treeImg/loading.gif'
};
var setTimeWaiting;
function onGetTree(value,Node)
{
if(Node.LoadingImg!=null)
{
//容错处理,因为setTimeWaiting有200ms的延迟,如果在延迟时间里双击图标,不致出错,虽然这种情况很少出现(追求完美)
Node.LoadingImg.src=nodeImgs.fopen;
Node.LoadingImg.src1=nodeImgs.fclose;
Node.nodeStateImg.src=nodeImgs.nodeOpened;
Node.nodeStateImg.src1=nodeImgs.nodeOpener;
Node.style.display="block";
}
for(var i=0;i<value.length;i++)
{
var ul = document.createElement("ul");
ul.className="Level_1";
var li2 = document.createElement("li");
li2.innerHTML=value[i].MenuName;
li2.className='li_txt';
var li1 = document.createElement('li');
for(var j=0;j<value[i].MenuLevel;j++)
{
var img = $c("img");
img.src=nodeImgs.nodeBlank;
li1.appendChild(img);
}
if(value[i].ChildCount!=0) //如果还有子节点
{
var img1 = document.createElement('img');
img1.src=nodeImgs.nodeOpener;
img1.src1=nodeImgs.nodeOpened;
var _id=value[i].MenuId;
var img2 = document.createElement('img');
img2.src=nodeImgs.fclose;
img2.src1=nodeImgs.fopen;
img1.onclick=(function(img1,img2,_id,ul){
return function(){
if(img2.src.indexOf(nodeImgs.nodeLoading)>0){return;}
var div = ul.nextSibling;
if(div==null||div.nodeName.toLowerCase() !="div")
{
var div=document.createElement("div");
div.style.display="block";
div.style.width="110%";
div.nodeStateImg=img1;
div.LoadingImg=img2;
div.setTimeWaiting=setTimeout(function(){img2.src=nodeImgs.nodeLoading;},200);
insertAfter(div,ul);
BGMService.GetChildMenus(_id,function(value){onGetTree(value,div);},onFailure);
}
else
{
switchAnttity(img1);
switchAnttity(img2);
if(div.style.display=="block")
{
div.style.display="none";
}
else
{
div.style.display="block";
}
}
}})(img1,img2,_id,ul);
li2.onclick=img1.onclick;
}
else
{
var img1 = document.createElement('img');
var img2 = document.createElement('img');
img2.src=nodeImgs.file;
img1.src=nodeImgs.nodeBlank;
var _src=value[i].Url;
li2.onclick=(function(text,_src){return function(){frame.addNewTab( text,text,_src);}})(value[i].MenuName,_src);
}
clearTimeout(Node.setTimeWaiting);
li1.appendChild(img1);
li1.appendChild(img2);
ul.appendChild(li1);
ul.appendChild(li2);
Node.appendChild(ul);
}
function switchAnttity(o)
{
var temp=o.src;
o.src=o.src1;
o.src1=temp;
}
}
function onFailure(value)
{
alert(value.get_message());
}
}
tree.prototype={
}