蛙蛙推荐:在网页上显示流程图
摘要:在做工作流应用的时候,一般都有工作流跟踪的功能,就是说让用户知道自己处理的这步在整个流程中的位置,以及整个流程的走向。如果是winform,可以用一些控件来显示定义好的流程图让用户看,而在web上就不太展示了,这里不用svg,vml也不用flash和servlight就能给用户展现流程图。
在本文中你将学到
1、怎样用xmlhttp来获远程的xml文档
2、javascript使用xpath查询xml文档
3、使用dhtml在网页上画矢量图
4、css相对定位容器中的绝对定位的灵活应用
5、组合以上技术,在网页中显示流程图
思路:一般的工作流软件都有流程设计的工具,大多工具通过可视化的设计工具来定义流程,完了能生成一个XML的文件,XML文件里一般有流程元素的位置信息,我们用xmlhttp异步读取这个XML文件,读取流程节点和节点之间的连线,然后用dhtml画出来,我们可以在网页合适的地方放置一个div,设置position: relative;当作画布,里面的活动和连线都相对这个画布来放置,这样你可以在你的工作流应用的一个固定地址来显示用户当前执行的工作流定义图,及当前执行在那一步。
比如我们的流程定义的XML文件如下(我瞎编的,我发现wf的xoml没有位置信息,汗):
<Model version="1.0" Name="Model0">
<Items>
<Startup ID="3768ce9e-50b2-45e5-8a10-4cf5518354ba" Width="44" Height="44" Top="44" Left="44" Name="Startup1" Label="请假" />
<Finish ID="a6745fb4-7164-4ea0-b1d0-851cff865477" Width="44" Height="44" Top="286" Left="22" Name="Finish1" Label="结束" />
<Activitie ID="f913dbaa-8677-496c-9424-9ada4d812e64" Width="44" Height="44" Top="154" Left="132" Name="部门领导审批" Label="部门领导审批" />
<Activitie ID="c0ec8743-d978-461a-8a12-253be46f27cc" Width="44" Height="44" Top="242" Left="132" Name="公司领导审批" Label="公司领导审批" />
<Linkline ID="2203f080-4220-4098-a460-c91165c0233f" P1_X="88" P1_Y="66" P2_X="132" P2_Y="176" />
<Linkline ID="020f0d97-244a-454c-864c-85f43e5c47a2" P1_X="132" P1_Y="176" P2_X="132" P2_Y="264" />
<Linkline ID="eb30702d-bfa9-4a22-95d2-07b00ecde2dc" P1_X="132" P1_Y="264" P2_X="66" P2_Y="308" />
<Linkline ID="7a3e5340-e45d-4975-ad99-81a661fc5d25" P1_X="132" P1_Y="176" P2_X="66" P2_Y="308" />
</Items>
</Model>
这是一个请教的流程,如果请假小于3天,直接部门领导审批就可以了,如果大于3天,部门领导审批后要再上报给公司领导审批,流程才算走完。这样这个流程就有两条路径,一条是“请假-部门领导审批-结束”,一条是“请教-部门领导审批-公司领导审批-结束”。
我们先放置好画布,并引入画矢量图的js开源脚本库wz_jagraphics.js(这个库大家可以搜索下,很容易找到,也很出名,兼容ff和ie)
<head>
<title>流程图</title>
<script type="text/javascript" language="javascript" src="graphics.js"></script>
</head>
<body>
<div id="myCanvas" style="position: relative; height: 500px; 250px; border: solid 1px black;
background-color: White;">
</div>
</body>
</html>
画布的名称是myCanvas,位置信息用css设置的,其中布局设置成relative。
然后我们用xmlhttp获取XML文件,并在获取文件完成的回调里调用私有方法来画开始,结束,活动和活动之间的线,代码如下。
function YunAjax(url,pars,method,onComplete,asynchronous)
{
var xmlHttp;
if(window.ActiveXObject)
{
try
{
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
else if(window.XMLHttpRequest)
{
xmlHttp = new XMLHttpRequest();
}
xmlHttp.onreadystatechange = function()
{
if(xmlHttp.readyState == 4)
{
onComplete(xmlHttp);
}
}
if(method.toLowerCase() == "get")
{
url = url+"?"+pars;
xmlHttp.open("GET",url,asynchronous);
xmlHttp.send(null);
}
else
{
xmlHttp.open("POST",url,asynchronous);
xmlHttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xmlHttp.send(pars);
}
}
function onAjaxComplete(rsp)
{
var doc = rsp.responseXML;
var elm = doc.documentElement.selectNodes("/Model/Items/Linkline");
drawStartup(doc.documentElement.selectSingleNode("/Model/Items/Startup"));
drawFinish(doc.documentElement.selectSingleNode("/Model/Items/Finish"));
for(var i = 0; i< elm.length;i++)
{
drawLinkline(elm[i]);
}
elm = doc.documentElement.selectNodes("/Model/Items/Activitie");
for(var i = 0; i< elm.length;i++)
{
drawActivitie(elm[i]);
}
jg.paint();
}
var modeurl = "http://localhost/WebApplication1/Model0.xml";
var currentNodeId = "c0ec8743-d978-461a-8a12-253be46f27cc";
YunAjax(modeurl,"","get",onAjaxComplete,false);
YunAjax方法是从网上找的一个简化的ajax获取远程文件的辅助方法,onAjaxComplete是在获取文件完毕的时候执行的回调,在那里面,我们用XPATH解析获取的XML文件,找出流程的开始、结束,活动和连线等,然后就是把解析出来的xmlNode传给一些私有方法去在画布上画出来,因为firefox下的xpath支持不太好,所以这个函数不支持firefox,有兴趣的朋友可以自己修改让它兼容firefox,其中modeurl表示流程的远程位置,currentNodeId表示当前执行的节点ID,这两个变量实际应用中应该动态生成,最后我们看一下几个画图的子方法。
function drawStartup(obj)
{
var X, Y, width, height ;
X = parseInt(obj.getAttribute("Left"));
Y = parseInt(obj.getAttribute("Top"));
width = parseInt(obj.getAttribute("Width"));
height = parseInt(obj.getAttribute("Height"));
jg.drawEllipse(X,Y,36,36);
var label = obj.getAttribute("Label");
jg.drawString(label,X,Y+40);
}
function drawFinish(obj)
{
jg.setColor("#82ED24");
jg.setStroke(3);
var X, Y, width, height ;
X = parseInt(obj.getAttribute("Left"));
Y = parseInt(obj.getAttribute("Top"));
width = parseInt(obj.getAttribute("Width"));
height = parseInt(obj.getAttribute("Height"));
jg.fillEllipse(X,Y,36,36);
var label = obj.getAttribute("Label");
jg.drawString(label,X,Y+40);
}
function drawLinkline(obj)
{
jg.setColor("#000000"); // 选择蓝色
jg.setStroke(1);
var x1,y1,x2,y2;
x1 = parseInt(obj.getAttribute("P1_X"));
y1 = parseInt(obj.getAttribute("P1_Y"));
x2 = parseInt(obj.getAttribute("P2_X"));
y2 = parseInt(obj.getAttribute("P2_Y"));
jg.drawLine(x1,y1,x2,y2);
jg.setStroke(1);
}
function drawActivitie(obj)
{
jg.setColor("#FF0004");
var X, Y, width, height ;
X = parseInt(obj.getAttribute("Left"));
Y = parseInt(obj.getAttribute("Top"));
width = parseInt(obj.getAttribute("Width"));
height = parseInt(obj.getAttribute("Height"));
var label = obj.getAttribute("Label");
var ID = obj.getAttribute("ID");
if(ID != currentNodeId)
{
jg.drawRect(X,Y,36,36);
jg.drawString(label,X,Y+40);
}
else
{
jg.fillRect(X,Y,36,36);
jg.drawString(label,X,Y+40);
}
}
我们用空心圆表示流程的开始,实心圆表示流程的结束,方框表示流程之间的活动,如果方框是实心的,表示现在正在执行到这一步,代码很简单,一看便知,关于那个画图的库,大家可参考如下网址
http://dev.csdn.net/article/26/26606.shtm
最后截图效果如下
后续:图画的不太漂亮,但思路是这个思路,呵呵,大家可以用一些图片去替换方块和圆圈,可以想办法把线段变成箭头,可以考虑用一些ff下的xpath方法让这个程序可以跑在firefox下,好像yui里的一个组件就有xpath的功能。