如图例:
此菜单跟之前提供的outlook菜单一样来自于sitemap设置
由于菜单是放在FrameSet中的一个frame上,因此弹出的子菜单不能用DIV的方式来实现,而是采用window.createpopup()的方式弹出一个窗口,并且在窗口中输出HTML来实现菜单内容的展现和行为.
采用此方法用到的基本知识点:
弹出窗口:
var oPopup= window.createPopup();
oPopup.innerHTML = "";
oPopup.document.write(sHTML); //写入输出内容
oPopup.show(left,top,width,height,document.body);
隐藏窗口:
oPopup.hide();
采用createpopup的方式来实现无限级别的弹出菜单需要注意以下细节和设计技巧:
1. 一个window同时只能弹出一个窗口,弹出另一个窗口的时候之前的窗口会自动关闭.
解决思路: E8.Net中菜单输出的脚本为一个类文件,类中设置了一个数组对象,保存每一级窗口创建的createpopup,用数组方式保存,在不同级别的弹出时用到对应窗口位置的弹出对象.
oPopup=E8MenuConfig.WindowsList[deep]; //deep为深度
if(null==oPopup)
{
oPopup=obj.document.parentWindow.window.createPopup();
}
这样可以保证每一级菜单弹出窗口不受影响
2.如何计算弹出窗口的位置,会遇到上级菜单已经靠近屏幕的情况.
解决思路:由于弹出窗口的位置是相对位置,需要考虑到相对屏幕有效位置的计算参考,把最上一级相对屏幕宽度(screen.availWidth)的计算结果带入到递归调用中去,无论哪一级的菜单均可比较到屏幕相对宽度(screen.availWidth)
if(deep > 1)
{
left += obj.offsetWidth - 3;
//如果到了屏幕的右边,则从左边开始
if(topLeft + width * 2 > screen.availWidth)
{
left = left - obj.offsetWidth - width + 3;
}
}
3.如何设计无限级菜单的数据源已保证递归调用的可行性.
解决思路:菜单采用2维数组作为数据源,由于不知道可能会产生的层次数量,需要采用递归调用中根据一个参数不同执行相同的代码能找到当前弹出窗口内容.
数组参考:
arrOperateMenu: MenuCode, SupMenuCode, MenuName, MenuUrl, Level, HasChild
menuCode 和SupMenuCode 为父子关系链接,弹出窗口函数 以SupMenucode为入口,下一级以MenuCode参数为入口,保证到循环的可行性
</ script >< script language = " javascript " >
var arrOperateMenu = new Array();
.
arrOperateMenu[ 17 ] = new Array( " 50202 " , " 502 " , " 常用类别管理 " , " /NewITSM/Forms/frmCatalogMng.htm " , 2 , false );
arrOperateMenu[ 18 ] = new Array( " 50203 " , " 502 " , " 服务单位管理 " , " /NewITSM/AppForms/frmBr_MastCustomerMain.aspx " , 2 , false );
arrOperateMenu[ 19 ] = new Array( " 50204 " , " 502 " , " 供应商管理 " , " /NewITSM/Provide/frmPro_ProvideManageMain.aspx " , 2 , false );
arrOperateMenu[ 20 ] = new Array( " 50205 " , " 502 " , " 桌面项管理 " , " /NewITSM/mydestop/frmEa_DefineMainPageMain.aspx " , 2 , false );
arrOperateMenu[ 21 ] = new Array( " 50206 " , " 502 " , " 通用表单配置 " , " /NewITSM/AppForms/frmappfieldconfig.aspx " , 2 , false );
arrOperateMenu[ 22 ] = new Array( " 50207 " , " 502 " , " 所有管理流程 " , " /NewITSM/Forms/form_all_flowmodel.aspx " , 2 , false );
arrOperateMenu[ 23 ] = new Array( " 50208 " , " 502 " , " 常用流程配置 " , " /NewITSM/Forms/form_flowmodel_set.aspx " , 2 , false );
arrOperateMenu[ 24 ] = new Array( " 502 " , " 5 " , " 基础资料 " , "" , 1 , true );
arrOperateMenu[ 25 ] = new Array( " 503 " , " 5 " , " 客户管理 " , " /NewITSM/AppForms/frmBr_ECustomerMain.aspx " , 1 , false );
arrOperateMenu[ 26 ] = new Array( " 504 " , " 5 " , " 资产管理 " , " /NewITSM/EquipmentManager/frmEqu_Main.htm " , 1 , false );
arrOperateMenu[ 27 ] = new Array( " 505 " , " 5 " , " 资产配置项管理 " , " /NewITSM/EquipmentManager/frmEqu_SchemaItemsMain.aspx " , 1 , false );
arrOperateMenu[ 28 ] = new Array( " 506 " , " 5 " , " 资产类别管理 " , " /NewITSM/EquipmentManager/frmSubject.htm " , 1 , false );
E8MenuConfig.Items = arrOperateMenu; </ script >
创建数组的脚本为后台代码根据sitemap内容和权限管理输出的
4.设计弹出窗口的自动隐藏逻辑,保证用户的使用便利性.
解决思路: 弹出窗口输出的HTML中加入一段自动隐藏的代码.
strTemp += " function HidPopup(){if(blnSubShow==false){if(parent.blnSubShow!=null) parent.blnSubShow=false;if( " + parentTreeNode + " .E8MenuConfig.WindowsList[ " + deep + " ]!=null) " + parentTreeNode + " .E8MenuConfig.WindowsList[ " + deep + " ].hide();}} " ;
strTemp += " </script> " ;
strTemp += " <body leftmargin=/ " 0 / " topmargin=/ " 0 / " scroll=/ " no/ " style=/ " border:solid menu 0pxpx;/ " οnmοuseοver=/ " clearTimeout(parent.popt); if (parent.blnSubShow != null ) parent.blnSubShow = true ;/ " οnmοuseοut=/ " parent.popt = setTimeout( ' HidPopup() ' , 10 );/ " >/n " ;
此代码中使用到一些技巧, 当有下一级菜单弹出时, blnSubShow = true, 则自动执行的代码中不会隐藏窗口,一旦下级菜单隐藏后,上级菜单的 blnSubShow 为true,自动执行的代码会隐藏当前窗口.
5. siteMap内容如何与脚本文件结合
为了保证与OUTLOOK风格菜单的兼容性( 登陆时可以选择), E8.Net中做了特殊的处理方式.根据SiteMap内容,权限及是否超级管理员专门做了后台代码进行输出,主要内容是一级菜单的HTML脚本和生成数组的脚本.
(非E8.Net用户使用这个E8NetMenu.js的时候可以根据数组格式自行开发相应代码和一级菜单的HTML脚本)
6. 其它注意事项不一一描述,此代码经过将近3天及加班的调试和完善,全部的脚本文件E8NetMenu.js 文件内容如下,供大家参考
{
if ( null == window.fe005e9f_e51b_4307_88ff_b4b150368fd4)
{
window.fe005e9f_e51b_4307_88ff_b4b150368fd4 = 0 ;
}
window.random = function()
{
var s = " RID " ;
var d = new Date();
s += d.getYear();
s += d.getMonth();
s += d.getDate();
s += d.getHours();
s += d.getMinutes();
s += d.getSeconds();
s += d.getMilliseconds();
s += parseInt(Math.random() * 1000000000000 );
s += (window.fe005e9f_e51b_4307_88ff_b4b150368fd4 ++ );
return s;
}
}
if ( null == window.absoluteLocation)
{
window.absoluteLocation = function(element, offset)
{ var c = 0 ; while (element) { c += element[offset]; element = element.offsetParent; } return c;
}
}
if ( null == window.$)
{
window.$ = function()
{
var elements = new Array();
for (var i = 0 ; i < arguments.length; i ++ )
{
var element = arguments[i];
if ( typeof element == ' string ' )
element = document.getElementById(element);
if (arguments.length == 1 )
return element;
elements.push(element);
}
return elements;
}
}
if ( null == window.E8MenuConfig)
{
window.E8MenuConfig = function()
{
this .id = random();
}
window.FrameMenu = new E8MenuConfig(); // create a new instance
E8MenuConfig.Items = new Array();
E8MenuConfig.WindowsList = new Array();
E8MenuConfig.TopWindow = window.createPopup();
E8MenuConfig.ShowSubMenu = function(MenuId,obj,img,deep,topLeft)
{
var strTemp = "" ;
var MenuUrl;
var blnHasAdd = false ;
var topL = topLeft;
var LineCount = 0 ; // 菜单行数
var MaxCharCount = 0 ; // 每行字数
if (obj != selectObj && deep == 1 )
{
// 第一层改变背景
obj.background = " images/ " + img;
}
var parentTreeNode = " parent " ;
for (var i = 0 ;i < (deep - 1 );i ++ )
{
parentTreeNode += " .parent " ;
}
var left = absoluteLocation(obj, ' offsetLeft ' );
var top = absoluteLocation(obj, ' offsetTop ' );
if (deep <= 1 )
{
if ((obj == document || obj == document.body ) && event != null )
{
left = event .x;
top = event .y;
}
else
{
top += obj.offsetHeight - 4 ;;
}
oPopup = E8MenuConfig.WindowsList[deep];
if ( null == oPopup)
{
oPopup = window.createPopup();
}
topL = left; // 记录最上层的left
}
else
{
oPopup = E8MenuConfig.WindowsList[deep];
if ( null == oPopup)
{
oPopup = obj.document.parentWindow.window.createPopup();
}
}
E8MenuConfig.WindowsList[deep] = oPopup;
E8MenuConfig.hideAllWindows(deep); // 关闭后面的窗口
for (var i = 0 ; i < E8MenuConfig.Items.length; i ++ )
{
if (E8MenuConfig.Items[i][ 1 ] == MenuId)
{
MenuUrl = E8MenuConfig.Items[i][ 3 ];
if (blnHasAdd == false )
{
strTemp += " <script>var blnSubShow = false; " ;
strTemp += " function HidPopup(){if(blnSubShow==false){if(parent.blnSubShow!=null) parent.blnSubShow=false;if( " + parentTreeNode + " .E8MenuConfig.WindowsList[ " + deep + " ]!=null) " + parentTreeNode + " .E8MenuConfig.WindowsList[ " + deep + " ].hide();}} " ;
strTemp += " </script> " ;
strTemp += " <body leftmargin=/ " 0 / " topmargin=/ " 0 / " scroll=/ " no/ " style=/ " border:solid menu 0pxpx;/ " οnmοuseοver=/ " clearTimeout(parent.popt); if (parent.blnSubShow != null ) parent.blnSubShow = true ;/ " οnmοuseοut=/ " parent.popt = setTimeout( ' HidPopup() ' , 10 );/ " >/n " ;
strTemp += " <table width=/ " 100 % / " height=/ " 100 % / " style=/ " border - collapse:collapse;/ " >/n " ;
}
blnHasAdd = true ;
LineCount ++ ;
if (E8MenuConfig.Items[i][ 2 ].length > MaxCharCount) MaxCharCount = E8MenuConfig.Items[i][ 2 ].length;
if (E8MenuConfig.Items[i][ 5 ]) // 有下级菜单
{
strTemp += " <tr><td bgcolor=/ " #C2D2E5/ " style=/ " border - top:#cccccc 1px solid;border - bottom:# 666666 1px solid;border - left:#cccccc 1px solid;border - right:# 666666 1px solid;mouse:hand; font - size:12px; color:# 000000 ;text - align:left;vertical - align:center;CURSOR:hand/ " οnmοuseοver=/ " this .bgColor = ' #dfdfdf ' ; " + parentTreeNode + " .E8MenuConfig.ShowSubMenu( ' " + E8MenuConfig.Items[i][0] + " ' , this , '' , " + (deep+1) + " , " + topL + " );/ " οnmοuseοut=/ " this .blnSubShow = false ; this .bgColor = ' #C2D2E5 ' ;/ " height=/ " 20 / " οnclick=/ "" + parentTreeNode + " .E8MenuConfig.GoToUrl( ' " + MenuUrl + " ' );/ " > " + E8MenuConfig.Items[i][ 2 ] + " >></td></tr> " ;
MaxCharCount += 2 ; // 多了 两个 >符号
}
else
{
strTemp += " <tr><td bgcolor=/ " #C2D2E5/ " style=/ " border - top:#cccccc 1px solid;border - bottom:# 666666 1px solid;border - left:#cccccc 1px solid;border - right:# 666666 1px solid;mouse:hand; font - size:12px; color:# 000000 ;text - align:left;vertical - align:center;CURSOR:hand/ " οnmοuseοver=/ " this .bgColor = ' #dfdfdf ' ;/ " οnmοuseοut=/ " this .blnSubShow = false ; this .bgColor = ' #C2D2E5 ' ;/ " height=/ " 20 / " οnclick=/ "" + parentTreeNode + " .E8MenuConfig.GoToUrl( ' " + MenuUrl + " ' );/ " > " + E8MenuConfig.Items[i][ 2 ] + " </td></tr> " ;
}
}
}
if (blnHasAdd == true )
{
strTemp += " </table>/n " ;
strTemp += " </body>/n " ;
}
oPopup.document.body.innerHTML = "" ;
if (strTemp.length > 0 )
oPopup.document.write(strTemp);
// 显示子菜单
if (strTemp.length > 0 )
{
var LineHeight = 20 ; // 行高
// 定义打开的oPopup的宽度,高度
var width = (MaxCharCount + 1 ) * 12 ; // 一般为中文
if (width < 90 ) width = 90 ;
// 如果是2层或以上,初始位置
if (deep > 1 )
{
left += obj.offsetWidth - 3 ;
// 如果到了屏幕的右边,则从左边开始
if (topLeft + width * 2 > screen.availWidth)
{
left = left - obj.offsetWidth - width + 3 ;
}
}
var height = LineCount * LineHeight;
oPopup.show(left,top, width, height, document.body);
}
}
E8MenuConfig.hideAllWindows = function(deep)
{
for (var n = deep; null != E8MenuConfig.WindowsList && n < E8MenuConfig.WindowsList.length;n ++ )
{
if ( null != E8MenuConfig.WindowsList[n] && null != E8MenuConfig.WindowsList[n].hide)
{
E8MenuConfig.WindowsList[n].hide();
}
}
}
E8MenuConfig.GoToUrl = function(url)
{
E8MenuConfig.hideAllWindows( 0 );
if (url != "" )
window.top.MainFrame.location = url;
}
}
采用createpopup实现无限级别的弹出菜单的技术遗憾.
由于IE缺省安全设置中禁用了 "允许由脚本初始化窗口,不受大小位置的限制",如果遇到三级以上菜单时可能会出现窗口位置不受控制的情况. 需要打开此设置.
为产品推广应用增加了一些阻力.
在这里感谢E8.Net合作伙伴的朋友提供的参考资料和建议.
E8.Net 工作流平台提供了此功能全部的源代码,用户可以直接使用或参考,并在此基础上更加的完善。欢迎E8.Net工作流平台的老客户索取相关技术及全部代码
E8.Net工作流架构大量节约用户的开发成本为企业应用开发提供起点,提升软件生产力,欢迎访问:http://www.feifanit.com.cn/productFlow.htm