E8.Net工作流平台

致力于中国管理软件设计
.NET工作流平台||IT服务管理系统||ITIL管理思想执行工具||企业流程管理解决方案||ITSM解决方案
posts - 67, comments - 155, trackbacks - 9, articles - 33
  博客园 :: 首页 ::  ::  :: 订阅 订阅 :: 管理

2008年9月4日

     开发一个系统的过程中可能会碰到需要在浏览器端动态输出 javascript脚本的情况,前几天遇到一个怪事. 代码在IE7环境下 一切正常,一遇到IE6的浏览器就出错.找了很久才找到问题的根本原因,是动态输出的脚本中 包含有 "</script> "的脚本内容在IE6中被当成了 脚本结束的标记.

 

    代码如下:

        

Code

 

由于此代码是在 浏览器端动态输出的,因此 在IE7以下版本中, 把这句代码 : strTemp += "</script>"; 中当作了脚本结束标记,产生错误.

 

解决方法为 ,把此代码 改为 strTemp += "</ " + "script>";   即可

 

以后遇到动态输出脚本, 遇到标记语言,最好分开写.

 

 

 

 

posted @ 2008-09-04 09:06 苏康胜 阅读(79) | 评论 (1)编辑

2008年8月23日

     摘要: 在E8.Net平台中已经包含了根据Sitemap生成outlook风格菜单的全部源代码. 不过还有一些客户提出另一种下拉菜单的样式需求. 于是在E8.Net 上扩展了一种新的菜单实现模式.如图例:此菜单跟之前提供的outlook菜单一样来自于sitemap设置由于菜单是放在FrameSet中的一个frame上,因此弹出的子菜单不能用DIV的方式来实现,而是采用window.createpopup(... 阅读全文

posted @ 2008-08-23 11:42 苏康胜 阅读(407) | 评论 (0)编辑

2008年8月2日

 

ITIL实施中骆驼与兔子的区别

 

    记得有一次看《对话》,一位名人说过,西方跨国企业是骆驼,中国的企业是兔子。

ITIL这个概念已经在中国流行起来了,很多IT服务的企业开始热忠于ITIL的管理思想。ITIL管理思想是很了不起的,从人、流程、效率、监督、分析等各个角度为IT服务管理者理清了思路。ITIL思想已经成功应用于西方一些跨国企业。如IBMHP……

可是在中国实施ITIL 我们能按照管理骆驼的方法来管理兔子吗?

骆驼是足够大的,即使不吃不喝一段时间,也可以走过沙漠,可是兔子呢,必须边跑的时候边找吃的和喝的,否则就会饿死。兔子不可能因为像管理骆驼一样的管理就可以达到骆驼一样的骨架的。

但兔子比骆驼灵活,碰到机会拔腿就上,可以靠速度去获得利润和胜利。而骆驼靠的是充足的储备,可以用“亏损”去建立“ITIL标准流程”而带来的品牌效应。

这样的形容来区别中国企业跟国外企业现阶段状况是非常合适的,中国企业目前最需要的是效率和应变能力,管理依然重要,但目标的侧重点应该是不同的。

因此留给大家实施ITIL时的几个额外的思考是必要的。

1、 ITIL的标准流程是否真的适合企业目前的状况?可行性如何?企业资源是否支撑标准流程的执行?

2、 如何参考ITIL标准的流程思想来设计企业现阶段合理的流程并加强执行力?

3、 如何通过综合分析逐步完善各种处理流程,提升管理特性?

4、 企业需要通过ITIL真正解决的问题有哪些?ITIL帮助企业优化了哪些KPI指标?

5、 ITIL管理思想如何在企业发展过程中不断完善和指导?

。。。。。。

 

大部分中国企业现阶段最需要的是效率和非常强的应变能力,因此选择ITIL管理工具进行实施的时候,工作流引擎将会是一个非常重要的因素。

 

E8.HelpDesk帮助企业做好各个阶段的IT服务管理欢迎访问: http://www.feifanit.com.cn/

posted @ 2008-08-02 15:10 苏康胜 阅读(255) | 评论 (0)编辑

     摘要: 通过Cache机制实现通用的配置管理模块 .Net Web应用程序提供了很强大的 Web.Config功能,我们很多的系统可能已经习惯在Web.Config中进行配置,可是使用Web.Config进行一些配置,会有一些不太顺畅的特性,比如:修改Web.Config 后,Web应用程序会出现错误页面并且需要重新登录,Web.Config配置过程不是很方便,即使通过安装包进行Web.Config的设置... 阅读全文

posted @ 2008-08-02 09:52 苏康胜 阅读(141) | 评论 (0)编辑

     摘要: 通过Cache机制实现通用的配置管理模块 .Net Web应用程序提供了很强大的 Web.Config功能,我们很多的系统可能已经习惯在Web.Config中进行配置,可是使用Web.Config进行一些配置,会有一些不太顺畅的特性,比如:修改Web.Config 后,Web应用程序会出现错误页面并且需要重新登录,Web.Config配置过程不是很方便,即使通过安装包进行Web.Config的设置... 阅读全文

posted @ 2008-08-02 09:45 苏康胜 阅读(79) | 评论 (0)编辑

2008年7月8日

 

为企业软件开发团队插上隐形的翅膀

     当今社会发展的状况是企业对软件系统的依赖是越来越强,十多年前我们企业中使用的软件大部分只是财务系统、仓库管理系统,而今进销存、ERPOACRM、物流调度、客户服务、IT服务、SPS。。。。通过软件系统支撑的企业活动几乎已经涉及到了企业活动的每个细节。这样的情况下企业软件开发团队必然面临了巨大的压力,CIO们深深困惑的是IT需要怎样的预算、如何保证各种系统的稳定运行、如何在需求越来越复杂的情况下提高开发效率、业务人员人事变动时如何做好系统培训、企业经营策略发生变化时如何让软件系统快速适应新的经营策略。。。。。。

    传统的软件开发管理方法中需求分析、系统分析、系统设计、系统开发、系统测试、系统交付等按部就班的做法为我们提供了稳定、可靠的开发管理策略,但实际情况下,这种模式更多时候像是只骆驼,慢慢的行走、迟钝的响应很难适应企业面临的社会竞争需求及市场环境的变化,毕竟软件是必须去支撑业务的,而不是让业务等待软件系统的。如何做到多、快、好、省这必然成为CIO们必须思考并解决的一个问题。

    为企业软件开发团队插上隐形的翅膀,一是管理创新营造高效率的团队文化;二是技术创新设计企业应用水平解决方案快速响应企业应用需求的开发、维护;

一、管理创新营造高效率的团队文化

1具有明确且有挑战性的共同目标
  一个具有明确的而且有挑战性目标的团队比目标不明确或不具有很大的挑战性目标的团队效率高得多,通常技术人员往往会因为完成了某个明确的任务,而且这个任务的完成具有挑战性的意义而感到自豪,反过来团队成员为了获取这种自豪的感觉而更加积极的工作从而带来团队开发的高效率,如作为系统设计人员很清楚的知道在什么时候要做到什么,什么时候开始做,什么时候必须完成,为了完成工作必须面临哪些挑战,怎么解决这些困难等为设计出一个高质量的软件项目提供了重要保证,而模模糊糊的去设计一个系统或模模糊糊的就去编写代码是非常危险的,而且会为此付出高昂代价,因此高效的软件开发团队具有挑战性的共同目标。

2团队具有很强的凝聚力
  在一个高效的软件开发团队中,成员们凝聚为一个整体共同进行工作,他们是相互支持、互相交流、互相尊重的,而不是相互推卸责任、保守、相互指责的,在一些散乱的开发团队中往往存在这样的问题,一些程序员是比较保守的,明明知道另外的模块中需要用到一段与自己已经编写完成但有些难度的程序代码,他也不愿拿出来给其它程序员共享,不愿与系统设计人员交流,这样给项目的进度造成了些不可度量的因素。

3具有融洽的交流环境
  在一个开发团队中,每个人行使自己的职责,如需求分析人员制定需求规格说明、系统设计人员做系统概要设计和详细设计、项目经理配置项目开发环境并且制定项目计划等,但每个人的工作不可能做到完美的,如系统概要设计的文档可能有个别地方词不达意,做详细设计的时候就可能会造成误解,项目经理制定计划时可能忽略了某种风险的存在而造成执行者过于紧张的压力等等情况都需要大家通过交流、反馈的手段然后协商解决的,因此高效的软件开发团队是具有融洽的交流环境的,而不是那种简单的命令执行式的。

4具有共同的工作规范和框架
  高效软件开发团队具有规范性及共同框架的工作,对于项目管理具有规范的项目开发计划,对于分析设计具有规范和统一框架的文档及审评标准,对于代码具有程序规范条例,对于测试有规范且可推理的测试计划及测试报告等等。并且所有成员都明白自己的职责,知道必须完成什么计划?由谁来完成?什么时候开始?什么时候结束?按什么顺序?等,总之一个高效的开发团队无论是工作内容还是工作流程都具有不同程度的规范性和标准风格的框架。

5采用合理的开发过程
  软件的开发不同于一般商品的研发和生产,开发过程中会面临着各种难以预测的风险,比如需求的变化、人员的异动、技术的瓶颈、同行的竞争等,高效的软件开发团队往往是采用了合理的开发过程去控制开发过程中的风险、提高软件的质量、降低开发费用,这样的团队会根据自身的必要程度决定要执行哪些工作?如配置管理、资源管理、版本控制、代码控制等,团队还合理的分划并定义开发过程的里程碑,决定每项活动内容的底线和审评标准,决定各项活动的先后关系或迭代的关系等。总之高效的软件开发团队的开发过程的原则是高效率、高质量、低成本。

二、技术创新设计企业应用水平解决方案快速响应企业应用需求的开发、维护

1设计企业应用模型为水平解决方案提供依据
  技术人员往往容易一开始就容易陷入“先进”技术架构的陷阱,当掌握到某一个技术架构的时候才发现原来我们什么也没有做,其实软件系统是为企业应用服务的,因此采用或者设计一个解决方案之前必须分析企业应用模型,企业的目标是什么、管理涉及的业务模块有哪些、企业需要掌握哪些关键指标、企业的管理发展模式是什么等等,这些是一个企业产生到发展不是变化很快的部分、一般企业在组织结构、业务流程、权限管理方面变化比较快。因此分析和设计企业应用模型是为企业提供水平解决方案的基础。

2分析企业应用模型设计企业基础软件架构
  当今环境下的高效软件开发团队已经不太有时间像以前设计一个系统一样针对当个系统进行分析、设计、开发、维护了,因为在企业用户需要应用非常多的系统的时候,这种传统的方式必定是会加大用户的使用难度和维护难度的。软件开发团队必须分析企业应用模型去抽象、去设计企业的基础软件架构,比如建立统一的组织结构、权限管理模型为各个系统进行统一的服务、统一的维护;设计客户资料模块为各个系统提供数据服务;设计通知服务统一满足企业各个应用系统邮件、短信、其它方式的通知功能。。。。。。设计一个系统的之前我们需要规划好、用好这些基础的公共模块。

3选择一个适应文化的工作流平台去适应业务流程的变化

一个好的工作流引擎是把原来“已知”的需求当作“未知”、“可能”、“或许”的需求来设计的,对这些“非未知”的需求进行了“泛型”的归纳和抽象,从而利用OO编程思想中的一些设计模式设计出了合理、具扩展性又充分的业务处理接口,利用工作流平台的功能或流程应用架构及开发这些接口的具体实现可以创造出各种与流程处理有关的应用系统,工作流引擎往往也是提供了流程控制台,来设计具体的业务流程。当企业各种业务流程发生变化时不需要再次维护代码,通过工具就可以调整,大大降低企业IT运维成本。

4建立一个企业应用软件系统快速开发架构
  从程序员的角度我们开发一个功能必定会用到非常多的基础组件、这些组件跟具体业务无关、比如加密、编码、日志、邮件、字符串操作、SQL HELPER。。。。。。我们需要一个基础的架构把这些模块统一维护统一应用减少开发的工作量。

从开发维护的角度,我们的系统功能中会有很多功能点,比如选择人员、选择日期、样式表等等,要做到易用性是需要做相当的代码的,而且这些功能会在多个模块中出现,需要快速开发架构统一管理和维护这些基础控件,降低开发维护的工作量。

从应用软件风格的角度,我们会开发很多的软件系统,菜单、布局、皮肤、事项、通知等如何实现个性化、同时又在开发新的系统中不再需要单独开发、我们需要快速开发架构为各个系统提供这些公共的功能。

从用户应用习惯的角度,我们在使用各个系统的时候,统一的登录、统一的待办事项入口、统一的用户配置是使用者最需要的,以往记住多套密码,处理各项工作的时候要登陆多个系统已经让软件用户感到了非常的难受,我们需要一个快速架构为企业开发软件系统提供这些基础的功能。

5建立一个机制持续改善、积累重用的模式
  通过开发管理的方法持续完善我们的基础组件库、控件库、皮肤,为快速开发架构提供更多可以重用的模块,这样可以让我们的开发团队开发和维护的效率更高。

为企业软件开发团队插上这对隐形的翅膀,相信我们的团队能做到多、快、好、省为企业经营提供最好的服务,E8.Net工作流平台为企业应用开发提供了一个起点,帮助企业软件开发团队插上这对翅膀,欢迎访问: http://www.feifanit.com.cn/productFlow.htm

posted @ 2008-07-08 09:09 苏康胜 阅读(283) | 评论 (0)编辑

2008年6月27日

     ITIL v3 是一个巩固和提高ITIL最佳实践的过程,引入了部分概念,尤其是"生命周期"的概念, V3相比 V2特征在以下5个方面的主要区别

   1、 V2关注诸如服务台、事件、问题、变更、配置和风险管理流程,V3关注服务,因为流程是服务的附属物;
   2、 V2关注的是业务与IT的结合,V3则强调业务与IT的整合
   3、 V2关注的是价值链管理 ,V3 则强调价值网络
   4、 V2关注的是线性的服务目录,V3则强调动态的服务投资组合
   5、 V2关注的是流程一体化的集成,V3则强调全面服务管理的生命周期



   总体而言,V3更有战略意义。。。

          为本土企业提供本土化、低成本ITIL解决方案:http://www.feifanit.com.cn/productITSM.htm



posted @ 2008-06-27 16:25 苏康胜 阅读(130) | 评论 (0)编辑

2008年6月24日

微软推出SharePoint2003 ,SharePoint2007以来,一个新的技术Web Parts也随着推出来了。刚一接触到这个东西,作为了一个开发人员,感到十分的好奇,这虽然算不上一次技术上的革命,但对用户的体验来讲,却是一个实实在在的好东东,能给使用的用户带来使用软件的幸福感,自在感。一句话,真是太棒了!
   看了很多软件已经实现了这个功能,像google这种大公司也早就实现了这种功能,还有许多其它公司也实现了类似的功能。最近我们在做E8.HelpDesk For ITSM产品,这样的好东西怎么能放过了。。。
   Web Parts的实现有多种方式,由于对.Net 2.0的Web Parts不是很熟,我们用最方便快捷的javascrpt脚本来实现,实现的原理是用Iframe,用户可以自定义自己的桌面,每个Iframe里可以放入一个网页地址,具体要放什么网页,我们可以做一个管理界面,加一个自定义桌面表,可以根据自己的需要,不断的延伸这个功能,做到非常的灵活,强大,这里面有用的XmlHttp技术,让用户自定义自已的桌面时,休验到快速的效果,最后的效果,类似于google的定义功能。现在把实现的脚本代码贴出来,与大家共享,共同进步,这个功能已经在E8.Net工作流开发架构中提供全部源码,E8.Net工作流客户可以直接使用哦!
 

// Input 0
/**
 * get element
 * document.getElementById的封装

 * 如果浏览器不支持getElementById方法则返回null
 */
function _getElementById(a)
{
 return document.getElementById?document.getElementById(a):null
}
/**
 * get elements tag name
 * document.getElementsByTagName的封装

 * 根据tagName返回数组,*返回所有tag。

 * 如果浏览器不支持getElementsByTagName方法,则返回空数组

 */
function _getElementsByTagName(a)
{
 return document.getElementsByTagName?document.getElementsByTagName(a):new Array()
}

//标志浏览器是否为Safari
var isSafari=navigator.userAgent.indexOf("Safari")>=0;

/**
 * 一个标准的colArrayAX替换页面内容典范
 * 变量a为地址,aa为回掉处理函数

 */
function _sendXMLRequest(theURL,aa)
{
 var xmlHttpObj=getXMLHttpObj();
 if(!xmlHttpObj||isSafari&&!aa)
 {//特殊浏览器特殊照顾

  (new Image()).src=theURL;
 }
 else
 {//正常的浏览器,用XMLHTTP显示内容
  xmlHttpObj.open("GET",theURL,true);
  xmlHttpObj.setRequestHeader( "CONTENT-TYPE ", "application/x-www-form-urlencoded ");
  xmlHttpObj.send(null);
 }
}
/**
 * 拿到一个可用的XMLHttpRequest对象
 */
function getXMLHttpObj()
{
 var a=null;
 if(window.ActiveXObject)
 {
  a=new ActiveXObject("Msxml2.XMLHTTP");
  if(!a)
  {
   a=new ActiveXObject("Microsoft.XMLHTTP");
  }
 }
 else if(window.XMLHttpRequest)
 {
  a=new XMLHttpRequest();
 }
 return a;
}

function _del(a)
{
 msg="确认不在桌面上显示该模块么?\n\n您可以通过自定义桌面恢复显示!";
  if(window.confirm(msg))
  {
   var module=_getElementById("module_"+a);
   if(module)
     module.style.display="none";
   aI();
 }
 return false
}

/**
 * 查找CSS,将class名字为a的aa自段定义值为ab
 * 例如setCSSAttrib("medit","display", "none");
 * 则代表将.media的display定义为none
 *
 */
function setCSSAttrib(clasName,attrName,attrValue)
{
 if(document.styleSheets)
 {//浏览器有styleSheets,查找CSS列表并修改

  clasName="."+clasName;
  for(var i=0;i<document.styleSheets.length;i++)
  {
   var classI=document.styleSheets[i];
   var rulesI=classI.rules;
   if(!rulesI)
   {
    rulesI=classI.cssRules;
    if(!rulesI){return}
   }
   for(var j=0;j<rulesI.length;j++)
   {
    if(rulesI[j].selectorText.toLowerCase()==clasName.toLowerCase())
    {
     rulesI[j].style[attrName]=attrValue
    }
   }
  }
 }
 else
 {//浏览器不支持styleSheets,一个元素一个元素地找并修改-_-b
  var elementI=_getElementsByTagName("*");
  for(var i=0;i<elementI.length;i++)
  {
   if(elementI[i].className==clasName)
   {
    elementI[i].style[attrName]=attrValue
   }
  }
 }
}


var aC="";

var _pnlo;
var _mod;
var ay=false;

function _upc()
{
// setCSSAttrib("medit","display",_pnlo||_uli?"":"none");
// setCSSAttrib("panelo","display",_pnlo?"":"none");
// setCSSAttrib("panelc","display",_pnlo?"none":"");
// setCSSAttrib("mod","display",_mod?"":"none");
// setCSSAttrib("unmod","display",_mod?"none":"");
 //如果_pl为true,并且_uli和_pnlo有一个为true,则设置mttl CSS的鼠标形状为移动
 //如果ay又为false,则把id为c_1、c_2和c_3的这三个元素构成一个数组,传给initHead()函数
 //initHead()函数负责对c_1、c_2、c_3这三个元素下的所有id以_h结尾的子元素设置拖拽事件代码
 //然后把ay设置为true确保initHead()函数只调用一次。之后对mttl CSS设置鼠标形状为move
// if(_pl&&(_uli||_pnlo))
 {
  if(!ay)
  {
   initHead([_getElementById("col_l"),_getElementById("col_r")]);
   ay=true
  }
  setCSSAttrib("TableHeader","cursor","move")
 }
}

var aq=0;

var colArray=[];
var ap=0;
var am=null;

/**
 * 如果am为null,将am创建为<div>标签,暂时先不显示,鼠标形状为move,

 * 背景为白色,底部padding为0px,直接创建在<body>下。最后返回am
 */
function createDiv()
{
 if(!am)
 {
  am=document.createElement("DIV");
  am.style.display="none";
  am.style.position="absolute";
  am.style.cursor="move";
  am.style.backgroundColor="#ffffff";
  am.style.paddingBottom="0px";
  document.body.appendChild(am)
 }
 return am
}

/**
 * 核心代码
 * al是这样一个对象,它有一个属性obj,默认为null,和init/start/drag/end/fixE五个方法
 * init() ---- 设置初始方法
 * 设置元素a的onmousedown事件响应为al.start方法,并设置am(那个直接隶属于
 *  <body>的隐藏<div>)的左边在页面左上角(如果没有设置过的话),设置上a的空拖拽
 *  事件
 * start() --- 拖拽开始事件

 *  设置全局变量aa和al.obj为事件源(同一时刻只能有一个box在drag状态)。得到隐藏div
 *  元素的坐标和当前事件的鼠标坐标,回掉aa变量在initHead()函数中注册的onDragStart()函数(将
 * 隐藏div内容填好,移动到鼠标位置)。将当前鼠标坐标记录在aa变量中。设置鼠标移动

 *  事件响应和鼠标抬起事件响应。

 * drag() ---- 拖拽中事件

 *  设置全局变量aa为事件源。得到当前鼠标坐标和移动中的div的位置,与上次鼠标坐标相比

 *  计算出偏移量,修改移动中的div的坐标。记录鼠标当前位置,回掉aa的onDrag()函数。设置

 *  al.obj为null,等待下个box的移动。

 * end() ----- 拖拽结束事件
 *  设置onmousemove和onmouseup不响应事件,回掉aa的onDragEnd()函数。

 * fixE()
 *  确保浏览器兼容性,保证变量a为event事件,并修正事件的layerX/Y(似乎没有用处)
 */
var al = {"obj":null,
 "init":function(a){
  a.onmousedown=al.start;
  if ( isNaN(parseInt(createDiv().style.left)) ) {
   createDiv().style.left="0px";
  }
  if ( isNaN(parseInt(createDiv().style.top)) ) {
   createDiv().style.top="0px";
  }
  a.onDragStart=new Function();
  a.onDragEnd=new Function();
  a.onDrag=new Function()
  },
 "start":function(a){
  var aa=al.obj=this;
  a=al.fixE(a);
  var ab=parseInt(createDiv().style.top);
  var ac=parseInt(createDiv().style.left);
  aa.onDragStart(ac,ab,a.clientX,a.clientY);
  aa.lastMouseX=a.clientX;
  aa.lastMouseY=a.clientY;
  document.onmousemove=al.drag;
  document.onmouseup=al.end;
  return false
  },
 "drag":function(a){
  a=al.fixE(a);
  var aa=al.obj;
  var ab=a.clientY;
  var ac=a.clientX;
  var ad=parseInt(createDiv().style.top);
  var ae=parseInt(createDiv().style.left);
  var af,ag;
  af=ae+ac-aa.lastMouseX;
  ag=ad+ab-aa.lastMouseY;
  createDiv().style.left=af+"px";
  createDiv().style.top=ag+"px";
  aa.lastMouseX=ac;
  aa.lastMouseY=ab;
  aa.onDrag(af,ag,a.clientX,a.clientY);
  return false
  },
 "end":function(){
  document.onmousemove=null;
  document.onmouseup=null;
  al.obj.onDragEnd(parseInt(createDiv().style.left),parseInt(createDiv().style.top));
  al.obj=null
  },
 "fixE":function(a){
  if (typeof a=="undefined") {
   a=window.event;
  }
  if (typeof a.layerX=="undefined") {
   a.layerX=a.offsetX;
  }
  if (typeof a.layerY=="undefined") {
   a.layerY=a.offsetY;
  }
  return a
  }
};

var aw=false;
/**
 * 本函数作用是设置所有标题可拖动,给元素加入拖拽事件响应代码。

 * 本函数只执行一次,aw为true时函数直接返回。

 * 对第一列c_1、第二列c_2、第三列c_3做初始设置。每一列下均有若干id为m_x的<div>标签
 * 每一个<div>标签内容均为一个<table>,该table中有一个<td>命名为m_x_h,这就是可可拖拽的

 * 标题。得到这个<td>元素,加入拖拽事件代码,就是本函数的作用。

 */
function initHead(a)
{
 if(aw)return;
 aw=true;
 //设置全局变量colArray为当前要处理的列数组,也即三个id为c_1、c_2和c_3的<td>元素
 colArray=a;
 //数组colArray中的每个元素都要执行。其实数组colArray只有三个元素,c_1、c_2和c_3,也即第一/二/三列
 for(var i=0;i<colArray.length;i++)
 {//对所有c_x的子结点遍历,其实也就是命名为m_x的div标签。最后一个div标签有其它用处,
  //故此处length-1
  for(var j=0;j<colArray[i].childNodes.length-1;j++)
  {
   var module_i=colArray[i].childNodes[j];
   var head_i=_getElementById(module_i.id+"_head");
   if(!head_i)
    continue;

   //此刻,已经得到了id为m_x_h的<td>元素,即box的标题td
   //将整个大<div>记录在ad对象的module属性中,这个module属性是????
   head_i.module=module_i;
   //用al对象的init方法初始化可拖拽标题td。

   al.init(head_i);

   //得到m_x_h的<a>元素,即id为m_x_url的<a>
   var url_i=_getElementById(module_i.id+"_url");
   if(url_i)
   {//设置<a>的h属性为ad(即上层标题的<td>元素),这个h属性是????
    url_i.h=head_i;
    //当超级链接被点中,设置上层标题<td>的href和target属性

    //为当前超级链接的href和target属性。这样用户也可以拖超级链接

    url_i.onmousedown=function() {
     this.h.href=this.href;
     this.h.target=this.target;
    }
   }

   var more_i=_getElementById(module_i.id+"_more");
   if(more_i)
   {
      more_i.module=module_i;
      more_i.onmouseover=function() {var op_i=_getElementById(this.module.id+"_op");if(op_i) op_i.style.display="";}
      more_i.onmouseout =function() {var op_i=_getElementById(this.module.id+"_op");if(op_i) op_i.style.display="none";}
   }

   //核心代码:拖拽开始回掉函数

   //关闭定时器,通过之前记录的module属性得到整个大<div>元素,调用aA()
   //函数计算所有box的偏移值。记录

   head_i.onDragStart=function(af,ag) {
     //关闭定时器

     clearInterval(ap);
     //通过之前记录的module属性得到整个大box的<div>元素
     var module_i=this.module;
     //计算页面上所有其它box的偏移值

     aA(module_i);
     //将下一个box的<div>元素记录下来
     module_i.origNextSibling=module_i.nextSibling;
     //得到移动的<div>,指定到鼠标位置,从隐藏状态显示出来

     //使用alpha filter将透明度设置为80,填充好内容和CSS

     //createDiv()生成一个DIV
     var module_i_copy=createDiv();
     module_i_copy.style.left=getOffset(module_i,true);
     module_i_copy.style.top=getOffset(module_i,false);
     module_i_copy.style.height=module_i.offsetHeight;
     module_i_copy.style.width=module_i.offsetWidth;
     module_i_copy.style.display="block";
     module_i_copy.style.opacity=0.8;
     module_i_copy.style.filter="alpha(opacity=80)";
     module_i_copy.innerHTML=module_i.innerHTML;
     module_i_copy.className=module_i.className;
     //设置dragged为false
     this.dragged=false
    };

   //核心代码:拖拽中回掉函数
   //全部由aG函数实现拖拽过程中的移动和“补位”

   head_i.onDrag=function(af,ag) {
    setModulePos(this.module,af,ag);
    //设置dragged为true
    this.dragged=true
    };

   //核心代码:拖拽结束函数

   head_i.onDragEnd=function(af,ag) {
     if (this.dragged) {
      //被拖拽叻,设置动态回位效果,把box安定下来
      ap=aD(this.module,150,15)
     } else {
      //box仅仅被鼠标点叻一下超级链接,需要提供正常

      //的超级链接被点击效果
      ax();
      if (this.href) {
       if (this.target){
        window.open(this.href,this.target)
       } else {
        document.location=this.href
       }
      }
     }
     this.target=null;
     this.href=null;
     //拖拽工作最后一步,取回box内的内容
     if (this.module.nextSibling!=this.module.origNextSibling) {
      aI()
     }
    }
  }//second for end
 }//first for end
}
/**
 * 隐藏浮动的拖拽移动中<div>元素
 */
function ax()
{
 createDiv().style.display="none"
}
/** 设置拖拽结束后的box动态回位效果

 */
function aD(a,aa,ab)
{
 var ac=parseInt(createDiv().style.left);
 var ad=parseInt(createDiv().style.top);
 var ae=(ac-getOffset(a,true))/ab;
 var af=(ad-getOffset(a,false))/ab;
 return setInterval(function(){if(ab<1){clearInterval(ap);ax();return}ab--;ac-=ae;ad-=af;createDiv().style.left=parseInt(ac)+"px";createDiv().style.top=parseInt(ad)+"px"},aa/ab)
}
/**
 * 全局变量colArray为列c_1、c_2和c_3数组(也即三个<td>元素)

 * 计算所有可移动的大box(即<div>)距离页面左边的偏移量和
 * 距离页面上方的偏移量。对于当前拖拽的box,在其所在列中,
 * 所有在它下面的<div>的pagePosTop值需要减去当前拖拽box的高度

 */
function aA(a)
{
 for(var aa=0;aa<colArray.length;aa++)
 {
  var ab=0;
  for(var ac=0;ac<colArray[aa].childNodes.length;ac++)
  {
   var ad=colArray[aa].childNodes[ac];
   if(ad==a)
    ab=ad.offsetHeight;
   ad.pagePosLeft=getOffset(ad,true);
   ad.pagePosTop=getOffset(ad,false)-ab
  }
 }
}
/**
 * 得到某一元素距离页面左边或上边的偏移量

 */
function getOffset(obj,isLeftOffset)
{
 var offsetValue=0;
 while(obj!=null)
 {
  offsetValue+=obj["offset"+(isLeftOffset?"Left":"Top")];
  obj=obj.offsetParent
 }
 return offsetValue
}
/**
 * 核心代码,拖拽中处理函数。变量a为box(即<div>元素),aa和ab为偏移量
 */
function setModulePos(obj,posLeft,posTop)
{
 var module=null;
 var ad=100000000;

 //对每一列遍历

 for(var i=0;i<colArray.length;i++)
 {//对每一个<div>box遍历
  for(var j=0;j<colArray[i].childNodes.length;j++)
  {
   var module_i=colArray[i].childNodes[j];
   //对于正在移动中的box自身,不作处理

   if(module_i==obj)
    continue;
   //计算某些偏移量

   var ai=Math.sqrt(Math.pow(posLeft-module_i.pagePosLeft,2)+Math.pow(posTop-module_i.pagePosTop,2));
   if(isNaN(ai))
    continue;
   if(ai<ad)
   {
    ad=ai;module=module_i
   }
  }
 }

 //再适当的位置上添加当前移动中的box
 if(module!=null&&obj.nextSibling!=module)
 {
  module.parentNode.insertBefore(obj,module);
  //TODO: 这行代码好像不起什么作用

  DisplayModule(obj)
 }
}
/**
 * 在页面上显示变量a的父节点
 * TODO: 第一行代码有什么用?

 */
function DisplayModule(obj)
{
 obj.parentNode.style.display="none";
 obj.parentNode.style.display=""
}
/**
 * 构造要取回内容的URL
 */
function aI()
{
 var a="";
 for(var i=0;i<colArray.length;i++)
 {
  a+=a!=""?":":"";
  for(var j=0;j<colArray[i].childNodes.length-1;j++)
  {
   var module=colArray[i].childNodes[j];
   if(module.id=="" || module.style.display=="none")
      continue;
   a+=module.id.substring(7)+",";
  }
 }
 _sendXMLRequest("mydestop/frmXmlHttp.aspx?MYTABLE="+escape(a),null)
}


// Input 0
/**
 * get element
 * document.getElementById的封装

 * 如果浏览器不支持getElementById方法则返回null
 */
function _getElementById(a)
{
 return document.getElementById?document.getElementById(a):null
}
/**
 * get elements tag name
 * document.getElementsByTagName的封装

 * 根据tagName返回数组,*返回所有tag。

 * 如果浏览器不支持getElementsByTagName方法,则返回空数组

 */
function _getElementsByTagName(a)
{
 return document.getElementsByTagName?document.getElementsByTagName(a):new Array()
}

//标志浏览器是否为Safari
var isSafari=navigator.userAgent.indexOf("Safari")>=0;

/**
 * 一个标准的colArrayAX替换页面内容典范
 * 变量a为地址,aa为回掉处理函数

 */
function _sendXMLRequest(theURL,aa)
{
 var xmlHttpObj=getXMLHttpObj();
 if(!xmlHttpObj||isSafari&&!aa)
 {//特殊浏览器特殊照顾

  (new Image()).src=theURL;
 }
 else
 {//正常的浏览器,用XMLHTTP显示内容
  xmlHttpObj.open("GET",theURL,true);
  xmlHttpObj.setRequestHeader( "CONTENT-TYPE ", "application/x-www-form-urlencoded ");
  xmlHttpObj.send(null);
 }
}
/**
 * 拿到一个可用的XMLHttpRequest对象
 */
function getXMLHttpObj()
{
 var a=null;
 if(window.ActiveXObject)
 {
  a=new ActiveXObject("Msxml2.XMLHTTP");
  if(!a)
  {
   a=new ActiveXObject("Microsoft.XMLHTTP");
  }
 }
 else if(window.XMLHttpRequest)
 {
  a=new XMLHttpRequest();
 }
 return a;
}

function _del(a)
{
 msg="确认不在桌面上显示该模块么?\n\n您可以通过自定义桌面恢复显示!";
  if(window.confirm(msg))
  {
   var module=_getElementById("module_"+a);
   if(module)
     module.style.display="none";
   aI();
 }
 return false
}

/**
 * 查找CSS,将class名字为a的aa自段定义值为ab
 * 例如setCSSAttrib("medit","display", "none");
 * 则代表将.media的display定义为none
 *
 */
function setCSSAttrib(clasName,attrName,attrValue)
{
 if(document.styleSheets)
 {//浏览器有styleSheets,查找CSS列表并修改

  clasName="."+clasName;
  for(var i=0;i<document.styleSheets.length;i++)
  {
   var classI=document.styleSheets[i];
   var rulesI=classI.rules;
   if(!rulesI)
   {
    rulesI=classI.cssRules;
    if(!rulesI){return}
   }
   for(var j=0;j<rulesI.length;j++)
   {
    if(rulesI[j].selectorText.toLowerCase()==clasName.toLowerCase())
    {
     rulesI[j].style[attrName]=attrValue
    }
   }
  }
 }
 else
 {//浏览器不支持styleSheets,一个元素一个元素地找并修改-_-b
  var elementI=_getElementsByTagName("*");
  for(var i=0;i<elementI.length;i++)
  {
   if(elementI[i].className==clasName)
   {
    elementI[i].style[attrName]=attrValue
   }
  }
 }
}


var aC="";

var _pnlo;
var _mod;
var ay=false;

function _upc()
{
// setCSSAttrib("medit","display",_pnlo||_uli?"":"none");
// setCSSAttrib("panelo","display",_pnlo?"":"none");
// setCSSAttrib("panelc","display",_pnlo?"none":"");
// setCSSAttrib("mod","display",_mod?"":"none");
// setCSSAttrib("unmod","display",_mod?"none":"");
 //如果_pl为true,并且_uli和_pnlo有一个为true,则设置mttl CSS的鼠标形状为移动
 //如果ay又为false,则把id为c_1、c_2和c_3的这三个元素构成一个数组,传给initHead()函数
 //initHead()函数负责对c_1、c_2、c_3这三个元素下的所有id以_h结尾的子元素设置拖拽事件代码
 //然后把ay设置为true确保initHead()函数只调用一次。之后对mttl CSS设置鼠标形状为move
// if(_pl&&(_uli||_pnlo))
 {
  if(!ay)
  {
   initHead([_getElementById("col_l"),_getElementById("col_r")]);
   ay=true
  }
  setCSSAttrib("TableHeader","cursor","move")
 }
}

var aq=0;

var colArray=[];
var ap=0;
var am=null;

/**
 * 如果am为null,将am创建为<div>标签,暂时先不显示,鼠标形状为move,

 * 背景为白色,底部padding为0px,直接创建在<body>下。最后返回am
 */
function createDiv()
{
 if(!am)
 {
  am=document.createElement("DIV");
  am.style.display="none";
  am.style.position="absolute";
  am.style.cursor="move";
  am.style.backgroundColor="#ffffff";
  am.style.paddingBottom="0px";
  document.body.appendChild(am)
 }
 return am
}

/**
 * 核心代码
 * al是这样一个对象,它有一个属性obj,默认为null,和init/start/drag/end/fixE五个方法
 * init() ---- 设置初始方法
 * 设置元素a的onmousedown事件响应为al.start方法,并设置am(那个直接隶属于
 *  <body>的隐藏<div>)的左边在页面左上角(如果没有设置过的话),设置上a的空拖拽
 *  事件
 * start() --- 拖拽开始事件

 *  设置全局变量aa和al.obj为事件源(同一时刻只能有一个box在drag状态)。得到隐藏div
 *  元素的坐标和当前事件的鼠标坐标,回掉aa变量在initHead()函数中注册的onDragStart()函数(将
 * 隐藏div内容填好,移动到鼠标位置)。将当前鼠标坐标记录在aa变量中。设置鼠标移动

 *  事件响应和鼠标抬起事件响应。

 * drag() ---- 拖拽中事件

 *  设置全局变量aa为事件源。得到当前鼠标坐标和移动中的div的位置,与上次鼠标坐标相比

 *  计算出偏移量,修改移动中的div的坐标。记录鼠标当前位置,回掉aa的onDrag()函数。设置

 *  al.obj为null,等待下个box的移动。

 * end() ----- 拖拽结束事件
 *  设置onmousemove和onmouseup不响应事件,回掉aa的onDragEnd()函数。

 * fixE()
 *  确保浏览器兼容性,保证变量a为event事件,并修正事件的layerX/Y(似乎没有用处)
 */
var al = {"obj":null,
 "init":function(a){
  a.onmousedown=al.start;
  if ( isNaN(parseInt(createDiv().style.left)) ) {
   createDiv().style.left="0px";
  }
  if ( isNaN(parseInt(createDiv().style.top)) ) {
   createDiv().style.top="0px";
  }
  a.onDragStart=new Function();
  a.onDragEnd=new Function();
  a.onDrag=new Function()
  },
 "start":function(a){
  var aa=al.obj=this;
  a=al.fixE(a);
  var ab=parseInt(createDiv().style.top);
  var ac=parseInt(createDiv().style.left);
  aa.onDragStart(ac,ab,a.clientX,a.clientY);
  aa.lastMouseX=a.clientX;
  aa.lastMouseY=a.clientY;
  document.onmousemove=al.drag;
  document.onmouseup=al.end;
  return false
  },
 "drag":function(a){
  a=al.fixE(a);
  var aa=al.obj;
  var ab=a.clientY;
  var ac=a.clientX;
  var ad=parseInt(createDiv().style.top);
  var ae=parseInt(createDiv().style.left);
  var af,ag;
  af=ae+ac-aa.lastMouseX;
  ag=ad+ab-aa.lastMouseY;
  createDiv().style.left=af+"px";
  createDiv().style.top=ag+"px";
  aa.lastMouseX=ac;
  aa.lastMouseY=ab;
  aa.onDrag(af,ag,a.clientX,a.clientY);
  return false
  },
 "end":function(){
  document.onmousemove=null;
  document.onmouseup=null;
  al.obj.onDragEnd(parseInt(createDiv().style.left),parseInt(createDiv().style.top));
  al.obj=null
  },
 "fixE":function(a){
  if (typeof a=="undefined") {
   a=window.event;
  }
  if (typeof a.layerX=="undefined") {
   a.layerX=a.offsetX;
  }
  if (typeof a.layerY=="undefined") {
   a.layerY=a.offsetY;
  }
  return a
  }
};

var aw=false;
/**
 * 本函数作用是设置所有标题可拖动,给元素加入拖拽事件响应代码。

 * 本函数只执行一次,aw为true时函数直接返回。

 * 对第一列c_1、第二列c_2、第三列c_3做初始设置。每一列下均有若干id为m_x的<div>标签
 * 每一个<div>标签内容均为一个<table>,该table中有一个<td>命名为m_x_h,这就是可可拖拽的

 * 标题。得到这个<td>元素,加入拖拽事件代码,就是本函数的作用。

 */
function initHead(a)
{
 if(aw)return;
 aw=true;
 //设置全局变量colArray为当前要处理的列数组,也即三个id为c_1、c_2和c_3的<td>元素
 colArray=a;
 //数组colArray中的每个元素都要执行。其实数组colArray只有三个元素,c_1、c_2和c_3,也即第一/二/三列
 for(var i=0;i<colArray.length;i++)
 {//对所有c_x的子结点遍历,其实也就是命名为m_x的div标签。最后一个div标签有其它用处,
  //故此处length-1
  for(var j=0;j<colArray[i].childNodes.length-1;j++)
  {
   var module_i=colArray[i].childNodes[j];
   var head_i=_getElementById(module_i.id+"_head");
   if(!head_i)
    continue;

   //此刻,已经得到了id为m_x_h的<td>元素,即box的标题td
   //将整个大<div>记录在ad对象的module属性中,这个module属性是????
   head_i.module=module_i;
   //用al对象的init方法初始化可拖拽标题td。

   al.init(head_i);

   //得到m_x_h的<a>元素,即id为m_x_url的<a>
   var url_i=_getElementById(module_i.id+"_url");
   if(url_i)
   {//设置<a>的h属性为ad(即上层标题的<td>元素),这个h属性是????
    url_i.h=head_i;
    //当超级链接被点中,设置上层标题<td>的href和target属性

    //为当前超级链接的href和target属性。这样用户也可以拖超级链接

    url_i.onmousedown=function() {
     this.h.href=this.href;
     this.h.target=this.target;
    }
   }

   var more_i=_getElementById(module_i.id+"_more");
   if(more_i)
   {
      more_i.module=module_i;
      more_i.onmouseover=function() {var op_i=_getElementById(this.module.id+"_op");if(op_i) op_i.style.display="";}
      more_i.onmouseout =function() {var op_i=_getElementById(this.module.id+"_op");if(op_i) op_i.style.display="none";}
   }

   //核心代码:拖拽开始回掉函数

   //关闭定时器,通过之前记录的module属性得到整个大<div>元素,调用aA()
   //函数计算所有box的偏移值。记录

   head_i.onDragStart=function(af,ag) {
     //关闭定时器

     clearInterval(ap);
     //通过之前记录的module属性得到整个大box的<div>元素
     var module_i=this.module;
     //计算页面上所有其它box的偏移值

     aA(module_i);
     //将下一个box的<div>元素记录下来
     module_i.origNextSibling=module_i.nextSibling;
     //得到移动的<div>,指定到鼠标位置,从隐藏状态显示出来

     //使用alpha filter将透明度设置为80,填充好内容和CSS

     //createDiv()生成一个DIV
     var module_i_copy=createDiv();
     module_i_copy.style.left=getOffset(module_i,true);
     module_i_copy.style.top=getOffset(module_i,false);
     module_i_copy.style.height=module_i.offsetHeight;
     module_i_copy.style.width=module_i.offsetWidth;
     module_i_copy.style.display="block";
     module_i_copy.style.opacity=0.8;
     module_i_copy.style.filter="alpha(opacity=80)";
     module_i_copy.innerHTML=module_i.innerHTML;
     module_i_copy.className=module_i.className;
     //设置dragged为false
     this.dragged=false
    };

   //核心代码:拖拽中回掉函数
   //全部由aG函数实现拖拽过程中的移动和“补位”

   head_i.onDrag=function(af,ag) {
    setModulePos(this.module,af,ag);
    //设置dragged为true
    this.dragged=true
    };

   //核心代码:拖拽结束函数

   head_i.onDragEnd=function(af,ag) {
     if (this.dragged) {
      //被拖拽叻,设置动态回位效果,把box安定下来
      ap=aD(this.module,150,15)
     } else {
      //box仅仅被鼠标点叻一下超级链接,需要提供正常

      //的超级链接被点击效果
      ax();
      if (this.href) {
       if (this.target){
        window.open(this.href,this.target)
       } else {
        document.location=this.href
       }
      }
     }
     this.target=null;
     this.href=null;
     //拖拽工作最后一步,取回box内的内容
     if (this.module.nextSibling!=this.module.origNextSibling) {
      aI()
     }
    }
  }//second for end
 }//first for end
}
/**
 * 隐藏浮动的拖拽移动中<div>元素
 */
function ax()
{
 createDiv().style.display="none"
}
/** 设置拖拽结束后的box动态回位效果

 */
function aD(a,aa,ab)
{
 var ac=parseInt(createDiv().style.left);
 var ad=parseInt(createDiv().style.top);
 var ae=(ac-getOffset(a,true))/ab;
 var af=(ad-getOffset(a,false))/ab;
 return setInterval(function(){if(ab<1){clearInterval(ap);ax();return}ab--;ac-=ae;ad-=af;createDiv().style.left=parseInt(ac)+"px";createDiv().style.top=parseInt(ad)+"px"},aa/ab)
}
/**
 * 全局变量colArray为列c_1、c_2和c_3数组(也即三个<td>元素)

 * 计算所有可移动的大box(即<div>)距离页面左边的偏移量和
 * 距离页面上方的偏移量。对于当前拖拽的box,在其所在列中,
 * 所有在它下面的<div>的pagePosTop值需要减去当前拖拽box的高度

 */
function aA(a)
{
 for(var aa=0;aa<colArray.length;aa++)
 {
  var ab=0;
  for(var ac=0;ac<colArray[aa].childNodes.length;ac++)
  {
   var ad=colArray[aa].childNodes[ac];
   if(ad==a)
    ab=ad.offsetHeight;
   ad.pagePosLeft=getOffset(ad,true);
   ad.pagePosTop=getOffset(ad,false)-ab
  }
 }
}
/**
 * 得到某一元素距离页面左边或上边的偏移量

 */
function getOffset(obj,isLeftOffset)
{
 var offsetValue=0;
 while(obj!=null)
 {
  offsetValue+=obj["offset"+(isLeftOffset?"Left":"Top")];
  obj=obj.offsetParent
 }
 return offsetValue
}
/**
 * 核心代码,拖拽中处理函数。变量a为box(即<div>元素),aa和ab为偏移量
 */
function setModulePos(obj,posLeft,posTop)
{
 var module=null;
 var ad=100000000;

 //对每一列遍历

 for(var i=0;i<colArray.length;i++)
 {//对每一个<div>box遍历
  for(var j=0;j<colArray[i].childNodes.length;j++)
  {
   var module_i=colArray[i].childNodes[j];
   //对于正在移动中的box自身,不作处理

   if(module_i==obj)
    continue;
   //计算某些偏移量

   var ai=Math.sqrt(Math.pow(posLeft-module_i.pagePosLeft,2)+Math.pow(posTop-module_i.pagePosTop,2));
   if(isNaN(ai))
    continue;
   if(ai<ad)
   {
    ad=ai;module=module_i
   }
  }
 }

 //再适当的位置上添加当前移动中的box
 if(module!=null&&obj.nextSibling!=module)
 {
  module.parentNode.insertBefore(obj,module);
  //TODO: 这行代码好像不起什么作用

  DisplayModule(obj)
 }
}
/**
 * 在页面上显示变量a的父节点
 * TODO: 第一行代码有什么用?

 */
function DisplayModule(obj)
{
 obj.parentNode.style.display="none";
 obj.parentNode.style.display=""
}
/**
 * 构造要取回内容的URL
 */
function aI()
{
 var a="";
 for(var i=0;i<colArray.length;i++)
 {
  a+=a!=""?":":"";
  for(var j=0;j<colArray[i].childNodes.length-1;j++)
  {
   var module=colArray[i].childNodes[j];
   if(module.id=="" || module.style.display=="none")
      continue;
   a+=module.id.substring(7)+",";
  }
 }
 _sendXMLRequest("mydestop/frmXmlHttp.aspx?MYTABLE="+escape(a),null)
}

.NET开发实现类似Web Parts功能,超简单实现

posted @ 2008-06-24 11:01 苏康胜 阅读(167) | 评论 (0)编辑

2008年6月20日

价值是事物存在最好的理由之一,发展中企业IT的价值尤其重要,企业管理中投资预算是相当重要的,一般企业的预算跟企业发展或多或少有些规律,最近看到一个图,跟E8.NET工作流平台的一些客户之前所描述的现象非常相似。


    
企业对于IT预算的增长是随着企业收入的增长趋势的,但IT需求却一直是持续增长的,这也是企业CIO们感受非常明显的现象。

     企业的IT设备数量在增长

    企业的软件系统支持在增长

     企业的应用复杂度在增长

     企业的系统运行时间长了以后,常规的维护工作量在增长

     ……

     但随着企业发展到一定阶段IT预算却是在负增长

     CIO们的困惑显然就会存在,如何在不变的预算下为用户提供更有价值的IT支撑呢?

一般IT部门对于企业来说是有两个大方向的工作,一是企业的IT开发支持,为企业采购或开发业务支持系统和管理系统;二是系统管理支持,为企业提供系统及IT设备的运行维护提高IT的可用性和稳定性,保证业务流畅。

     本文中将重点讨论如何提供更有价值的业务支持系统和管理系统支撑。

     大家都知道一个业务系统的成本主要体现在开发 培训应用维护四个方面,在设计一个业务系统的时候如何做到在不变的预算下提供更有价值的软件产品呢?

     传统的项目管理中有一个平衡图:

     

     这个平衡图说明的一件事情是:产品特性效率成本是相互制约的,提升产品特性必定会牺牲效率或成本,降低成本必定会牺牲效率或产品特性,但这个制约平衡图明显跟企业IT预算的规律图有一定的冲突,预算不变的情况下,效率和产品特性需求会增长。CIO必须考虑一种方法到达这样的目标。

    要到达这样的目标一定需要从以下几个方面来考虑

1、降低开发成本

降低开发成本无非采用合理的开发管理模式、采用成熟的业务开发框架降低传统开发中的复杂度、整合现有资源减少重复开发。

企业中普遍可以整合的资源主要为企业的基础组织架构、流程管理平台、企业门户、报表服务、邮件平台、短信平台。一般企业CIO在设计企业的IT架构时这些往往是决定企业IT系统支撑开发成本的关键,因为这些资源普遍是可以在各个应用系统中重复利用的,不必要在每个系统中去重复开发这些模块,这些资源的产品设计和选择是相当重要的。

2、降低维护成本

降低维护成本,主要体现在两个方面的考虑:

a、软件系统采购后对开发商的依赖;

软件系统上线使用后,往往不可能需求不变的,经常需要随着企业经营策略的变化而调整软件的使用,比如企业组织结构发生变化、系统的权限管理策略发生变化。。。。。。软件供应商是否提供实现工具给使用者。

b、业务需求变化时的快速响应。

业务需求变化普遍是是企业流程和管理模式的变化,对于开发和管理这些变化是否非常方便和低成本,一个好的工作流引擎设计器已经考虑了各种各样的流程特性,满足企业目前、将来的各种流程需求。

3、降低软件的培训成本

降低软件培训成本,至少需要考虑以下几个方面

a、企业集成特性及单点登录

跟现有系统的集成是可以大大降低培训成本的。使用新系统的时候可以从现有系统自然链接,过度过去。

b、统一软件风格

统一的软件风格也是降低培训成本的,比如说一个选择人员的功能,如果各个系统中保持一致的使用方式,培训成本也是会降低的。

c、产品的易用性

易用性好的产品,一般会让用户不需要什么培训,很自然就明白如何使用,培训成本自然就低了。

4、降低软件的应用成本

     一个软件产品功能的描述可能没有多少区别,也许在用户使用的时候就会有一些区别了,一个好的产品可以让用户很少的输入变很方便的获取到详细的信息,用户录入越少或找到相关信息的途径越智能意味着使用成本越低。E8有个IT服务管理的产品,用户接到电话,自动会知道是哪个客户,哪个电脑出现问题,曾经发生过哪些维护事件,可以参考哪些知识。。。。。。虽然开发过程中做了很多细致的代码,但对于用户的使用成本大大降低了。查看电脑的历史配置时也是图形化的,用户鼠标移过马上显示做过哪些修改,谁修改的,经过了怎样的过程,用户一目了然,方便了用户自然降低了软件的应用成本

5、日积月累的管理模式

很早以前的一位经理说过,喝水的时候不要忘记挖一口井,这句话一直觉得非常有道理,软件的开发效率很大一部分程度取决于重用度,很多业务无关的组件、控件及通用的功能模块,在业务需求中出现的时候,管理上同时能做成可以重用并积累的模式,这样我们的开发成本将越来约低。

E8.Net工作流平台为.Net企业应用开发提供了一个起点。E8.Net工作流提升企业战略执行力,欢迎访问: http://www.feifanit.com.cn/

posted @ 2008-06-20 08:37 苏康胜 阅读(114) | 评论 (0)编辑

 

价值是事物存在最好的理由之一,发展中企业IT的价值尤其重要,企业管理中投资预算是相当重要的,一般企业的预算跟企业发展或多或少有些规律,最近看到一个图,跟E8.NET工作流平台的一些客户之前所描述的现象非常相似。


    
企业对于IT预算的增长是随着企业收入的增长趋势的,但IT需求却一直是持续增长的,这也是企业CIO们感受非常明显的现象。

     企业的IT设备数量在增长

    企业的软件系统支持在增长

     企业的应用复杂度在增长

     企业的系统运行时间长了以后,常规的维护工作量在增长

     ……

     但随着企业发展到一定阶段IT预算却是在负增长

     CIO们的困惑显然就会存在,如何在不变的预算下为用户提供更有价值的IT支撑呢?

一般IT部门对于企业来说是有两个大方向的工作,一是企业的IT开发支持,为企业采购或开发业务支持系统和管理系统;二是系统管理支持,为企业提供系统及IT设备的运行维护提高IT的可用性和稳定性,保证业务流畅。

     本文中将重点讨论如何提供更有价值的业务支持系统和管理系统支撑。

     大家都知道一个业务系统的成本主要体现在开发 培训应用维护四个方面,在设计一个业务系统的时候如何做到在不变的预算下提供更有价值的软件产品呢?

     传统的项目管理中有一个平衡图: