1、动态扩展平台扩展脚本编写指南EAS产品部技术架构部 贺召军 导读阐述基于动态扩展平台 DEP开发的扩展脚本编写指南和相关注意事项,通过本规范指导开发工作。适用读者实施人员、现场二次开发人员、研发中心客户化开发人员。适用范围金蝶 EASV7.0SP1。简介 3指定作用域 3变量 4函数 4使用扩展脚本 5第一章 上下文 51.1 插件上下文(pluginCtx ) 51.1.1 获取服务端上下文 .61.1.2 获取客户端上下文 .71.1.3 获取界面控件 .71.2 方法上下文(methodCtx) 81.2.1 获取方法参数 .81.2.2 获取方法返回值 .81.2.3 设置方法返回值
2、.81.2.4 方法上下文常用方法 .81.3 快速插入上下文脚本 9第二章 调用业务方法 92.1 调用方式 102.1.1 客户端调用业务方法方式 .112.1.2 服务器端调用业务方法方式 .132.2 快速插入业务方法调用 13第三章 自定义异常 143.1 定义自定义异常 143.2 脚本中调用自定义异常 143.2.1 服务器端调用异常方式 .143.2.2 客户端异常调用方式 .153.3 快速插入自定义异常调用 15第四章 自定义基础资料或单据 164.1 获取自定义基础资料和单据远程接口 .164.2 传入参数 .174.3 返回值类型 .184.4 示例脚本 .19第五章
3、给常用控件添加事件 205.1 F7 .205.2 CheckBox.215.3 ComboBox .215.4 Button.215.5 TextFiled215.6 Tree 235.7 TabledPane.235.8 Table 23第六章 使用函数库 246.1 定义函数库 .246.2 使用函数库 .26第七章 打点提示 快速编写脚本 277.1 环境准备 277.2 使用打点提示 29第八章 调试脚本 298.1 调试准备 .298.1.1 客户端调试 298.1.2 服务器端调试 .308.2 进行调试 .318.2.1 变量查看 328.2.2 使用脚本控制台 328.3 监
4、听事件方法内部调试 .338.4 调试完成 .33第九章 常见问题 349.1 在脚本里添加里添加了监听器,在代码里删除监听器的时候的报中断错误 .349.2 扩展脚本不能调用当前类的 private 方法,不能用 super 来调用父类方法 .359.3 扩展脚本中不能使用强制类型转换 .35附录一 关于 Rhino.37Rhino 语法简介 .37历史简介 37JavaScript 中的 “Java“ 38Rhino 和 java 语言的具体区别 39数组 39联合数组 39通过属性进行循环 40正则表达式 40函数显式声明 41添加方法 42添加更多的方法 42使用原型 43无类编码 4
5、3基于类的语言与基于原型的语言的比较 44附录二 案例脚本 45简介扩展脚本运行引擎为 Rhino,基于 javascript 语法,运行期解释执行。jvm 中的类的方法和属性均可应用于脚本中,调用方式类似于代码调试。比如下面一段脚本:/客户端设置编码规则脚本/定义引入变量var easNames = JavaImporter();/引入指定包easNames.importPackage(P.kingdee.eas.base.codingrule); /使用引入作用域with(easNames) /从上下文中获取编辑界面编辑对象var editData = pluginCtx.getDataO
6、bject();/取得控件var bizOrgUnit = pluginCtx.getKDBizPromptBox(“prmtSaleOrgUnit“).getValue();/远程方法调用获取编码var number = CodingRuleManagerFactory.getRemoteInstance().getNumber(editData, bizOrgUnit.getId();/设置 TextField 编码pluginCtx.getKDTextField(“txtNumber“).setText(number); 指定作用域在示例代码中var easNames = JavaImp
7、orter();easNames.importPackage(P.kingdee.eas.base.codingrule);和 java 中 import 类似,这里使用 JavaImporter()的 importPackage 引入包com.kingdee.eas.base.codingrule,CodingRuleManagerFactory 属于该包中的内容,但其处在with(easNames) 中,所以不需要指定其全路径,即可调用 getRemoteInstance()方法,如果需要引入其它包,则增加 easNames.importPackage(Packages.包路径);即可。如
8、果没使用with 指定作用域,则必须使用全名称(包名 +类名) 。注意:当引入的不同包路径下有重复的类名称情况下,在 with(easNames)作用域中,该类名在引用时也需要指定其全路径。变量使用 var 声明变量,声明时无具体类型运行期确定类型。例如 var editData = pluginCtx.getDataObject();声明 editData 为编辑界面的编辑对象。变量的定义声明遵循javascript 语法。函数使用 function 关键字开始函数的声明,在函数声明中看不到类型,包含参数和返回值,如下所示/创建值对象function createNewData(entity
9、BosType, entityPK)var objectValue = new com.kingdee.eas.ep.DataBaseCustomInfo();objectValue.setBOSType(entityBosType);objectValue.setPK(new com.kingdee.bos.metadata.MetaDataPK(entityPK);return objectValue;然后就可以调用了定义的函数了var bizInterface = com.kingdee.bos.BOSObjectFactory.createRemoteCommonBOSObject(n
10、ew com.kingdee.bos.metadata.MetaDataPK(entityPK);bizInterface.addnew(createNewData(entityBosType, entityPK); /新增在 javaScript 中没有类的概念,接口实现也使用关键字 function 声明。比如给一些控件增加监听器:/F7 控件增加值改变监听pluginCtx.getKDBizPromptBox(“prmtHROrg“).addDataChangeListener(function (event)com.kingdee.eas.util.client.MsgBox.show
11、Info (“DATA CHANGED:“ +event.getOldValue() + “$“ + event.getNewValue(););/KDTable 增加表格编辑事件监听pluginCtx.getKDTable(“kdtEntrys“).addKDTEditListener(function(event,methodName)if(methodName = “editStopped“)com.kingdee.eas.util.client.MsgBox.showInfo(“$EditStopped“););使用扩展脚本扩展定义:针对扩展点编写扩展脚本。扩展点:能够支持扩展脚本的公
12、用业务方法,需要改变其执行效果预期的地方。在实体或 ui 里扩展定义可编写扩展脚本,选择某个叶子节点即可编写针对该业务方法的前置脚本和后置脚本。前置脚本在业务方法被调用前被执行,后置脚本在业务方法返回后被执行。脚本中可以使用业务方法的参数,也可以设置业务方法的返回值。第一章 上下文扩展脚本中封装了两种上下文,插件上下文(pluginCtx)和方法上下文(methodCtx ) 。这两种上下文为系统内置,无需额外的声明或获取。pluginCtx 主要存放当前执行环境的相关信息,比如服务端的 Context,客户端的界面控件、界面状态、编辑对象等。methodCtx 存放当前操作的相关信息,包含方
13、法参数和方法返回值。1.1 插件上下文( pluginCtx)客户端和服务端的插件上下文存储的内容不一样,实现方式上,其分别对应于两个java 类,com.kingdee.eas.ep.app.BeanParam 和 com.kingdee.eas.ep.client.UIParam。这两个类中的方法均可以在脚本中直接使用。上下文都存在 get 和 put 方法。脚本中直接使用 get(key)获取值,put(key,value)设置值。1.1.1 获取服务端上下文服务端通过 pluginCtx.getContext()获取服务端上下文,比如:/服务端更新单据状态var imp = JavaI
14、mporter(); imp.importPackage(P.kingdee.eas.util.app); with(imp)/获取服务端上下文var ctx = pluginCtx.getContext();/获取方法参数var billId = methodCtx.getParamValue(0); var sql = “update T_SD_SALEORDER SET FBASESTATUS = “ + status + “ WHERE FID = “+billId+“;/执行 sql,使用服务器端上下文DbUtil.execute(ctx, sql); 服务器端上下文常见用法描述 服
15、务器端上下文用法获取当前用户 com.kingdee.eas.util.app.ContextUtil.getCurrentUserInfo(pluginCtx.getContext();获取 client 端 IP com.kingdee.eas.util.app.ContextUtil. getClientIP(pluginCtx.getContext();获取 client 端名称 com.kingdee.eas.util.app.ContextUtil.getClientName(pluginCtx.getContext();获取当前 CU com.kingdee.eas.util.a
16、pp.ContextUtil. getCurrentCtrlUnit(pluginCtx.getContext();获取当前财务组织(公司) com.kingdee.eas.util.app.ContextUtil. getCurrentFIUnit(pluginCtx.getContext();获取当前行政组织 com.kingdee.eas.util.app.ContextUtil.getCurrentAdminUnit(pluginCtx.getContext();获取当前组织 com.kingdee.eas.util.app.ContextUtil. getCurrentHRUnit
17、(pluginCtx.getContext();获取当前数据库类型 com.kingdee.eas.util.app.ContextUtil. getDbType (pluginCtx.getContext();获取当前组织单元 com.kingdee.eas.util.app.ContextUtil.getCurrentOrgUnit(pluginCtx.getContext();1.1.2 获取客户端上下文客户端插件上下文封装了部分常用的内容,具体如下:描述 获取客户端上下文获取界面上下文 java.util.Map getUIContext()获取组织上下文 com.kingdee.bo
18、s.Context getMainOrgContext()获取界面状态,比如新增、编辑java.lang.String getOprtState()获取当前编辑对象,编辑界面的 editDatacom.kingdee.bos.dao.IObjectValue getDataObject()获取界面绑定 com.kingdee.bos.appframework.databinding.DataBinder getDataBinder()获取规则处理 com.kingdee.eas.framework.client.UILifeCycleHander getLifeCycleHander()获取当
19、前界面实例 com.kingdee.bos.ui.face.IUIObject getUI()获取组织 pluginCtx.getUIContext().get(“sysContext“).getCurrentFIUnit()获取界面状态 pluginCtx.getUI().getOprtState();获取界面 Action pluginCtx.getUI().getActionManager().getAction(“actionName“)直接通过 pluginCtx 调用即可,比如 pluginCtx.getUI 即可获取当前界面实例。1.1.3 获取界面控件界面的控件可以通过 get
20、 + 控件类型 + ( + 控件名 +)的方式获得,比如获取单据分录的表格可以这样 pluginCtx.getKDTable(“kdtEntries“)。控件 示例f7 控件 pluginCtx.getKDBizPromptBox(“name“);容器控件 pluginCtx.getKDContainer(“name“);textField 控件 pluginCtx.getKDTextField(“name“);KDTextArea 控件 pluginCtx.getKDTextArea (“name“);KDPasswordField 控件 pluginCtx.getKDPasswordFie
21、ld (“name“);KDFormattedTextField 控件 pluginCtx.getKDFormattedTextField (“name“); KDDateTimeField 控件 pluginCtx.getKDDateTimeField (“name“);KDCheckBox 控件 pluginCtx.getKDCheckBox(“name“);KDRadioButton 控件 pluginCtx.getKDRadioButton (“name“);KDMenu 控件 pluginCtx.getKDMenu(“name“);KDMenuItem 控件 pluginCtx.ge
22、tKDMenuItem(“name“);KDTree 控件 pluginCtx.getKDTree(“name“);KDTreeView 控件 pluginCtx.getKDTreeView(“name“);KDList 控件 pluginCtx.getKDList(“name“);KDTimePicker 控件 pluginCtx.getKDTimePicker(“name“);KDDatePicker 控件 pluginCtx.getKDDatePicker (“name“);KDTabbedPane 控件 pluginCtx.getKDTabbedPane (“name“);1.2 方法
23、上下文( methodCtx)方法上下文存放了方法的相关信息,包括方法参数和方法返回值。方法上下文提供如下我们来看这个示例,删除主表记录时也删除对应表的纪录/服务端更新单据状态var imp = JavaImporter(); imp.importPackage(P.kingdee.eas.util.app); with(imp)var ctx = pluginCtx.getContext();var billId = methodCtx.getParamValue(0);/获取参数var isDone = methodCtx. getResultValue();/获取方法返回值是否成功执行v
24、ar status = 11;var sql = “delete T_SD_SALEORDER WHERE FBillID = “+billId+“;if(isDone)DbUtil.execute(ctx, sql);methodCtx.setResultValue(java.lang.Boolean.TRUE);/设置方法返回值1.2.1 获取方法参数如果想获得方法的某个参数,比如 delete(String pk)的参数,这可以通过methodCtx.getParamValue(0)来获得单据的 pk。示例中以var billId = methodCtx.getParamValue(0)
25、; 来获取参数需要删除单据的 id1.2.2 获取方法返回值描述:可以通过 methodCtx.getResultValue()来获得方法的返回值。用法:一般在后置脚本中使用,如示例代码中,通过状态方法是否成功执行来判断是否需要删除对于表的纪录:var isDone = methodCtx. getResultValue();1.2.3 设置方法返回值设置方法的返回值必须通过 methodCtx.setResultValue(value)来设置,不可以用类似方法返回值的方式写(return value ) 。如示例代码中设置方法返回值为 TRUE。methodCtx.setResultValu
26、e(java.lang.Boolean.TRUE)1.2.4 方法上下文常用方法描述 方法获取方法名 String getName()获取方法别名 String getAlias()获取方法参数类型 String getParamTypes()获取方法某个参数的类型 String getParamType(int index)获取方法参数值 Object getParamValues()获取方法某个参数值 Object getParamValue(int index)获取方法返回值类型 String getResultType()获取方法返回值 Object getResultValue()设
27、置方法返回值 void setResultValue(Object value)1.3 快速插入上下文脚本可以通过动态扩展平台的扩展定义中的上下文来快速编写一些常用脚本。如上图所示,当选择扩展点后,切换到上下文页签,选中前置或后置脚本,双击所需树结点,就可以自动生成模版脚本,例如双击当前用户生成模板脚本:com.kingdee.eas.util.app.ContextUtil.getCurrentUserInfo(pluginCtx.getContext(); 自动生成模板脚本双击这里第二章 调用业务方法在实体或 Faade 中新增一个业务方法后,如何调用该业务方法?自定义业务方法的调用是通过
28、工具类来实现的,该实现方式同样适用标准产品中已有的业务方法调用,下面将说明。2.1 调用方式调用自定义业务方法和标准产品已有的业务方法方式相同,下面我们在实体上新增一个自定义一个业务方法 helloWord。然后可到扩展定义中找到该新增方法的方法描述。返回值类型参数方法名2.1.1 客户端调用业务方法方式com.kingdee.bos.framework.BOClientTool.callCmethod(pk,methodSignature,args)元数据 PK(IMetaDataPK pk)方法名称(String methodSignature)参数对象数组(Object args)其中元
29、数据 PK 为实体的全名称,可从元数据检查获得:复制这里获取元数据全路径拷贝可获取方法描述方法名称请从该业务方法的扩展点的描述信息中拷贝获得,否则容易出错,如果方法没有返回值,注意名称前有空格,不要删除。比如:/客户端调用服务端方法var easNames = JavaImporter();easNames.importPackage(P.kingdee.bos.ctrl.extendcontrols);easNames.importPackage(P.kingdee.bos.util);easNames.importPackage(P.kingdee.bos.metadata);easNam
30、es.importPackage(P.kingdee.bos.framework);with(easNames)/定义所需参数/元数据 pkvar pk = new MetaDataPK(“com.kingdee.eas.basedata.assistant.Termsofapply“);/方法名称请从该业务方法的扩展点的描述信息中拷贝获得,否则容易出错, 如果方法/没有返回值,注意名称前有空格,不要删除var name = “java.lang.Boolean updateStatus(com.kingdee.bos.util.BOSUuid billId)“;/参数,以数组形式保存,js
31、数组定义格式请参见 附录.数组 /下面示例中为一个参数 BOSUuidvar params = BOSUuid.read(“2s5eatuISSqb7TuLMfibSMSKQjo=“);/使用客户端业务方法调用var result = BOClientTool.callCmethod(pk,name,params);MsgBox.showInfo(“Result is:“ + result);com.kingdee.eas.util.SysUtil.abort();2.1.2 服务器端调用业务方法方式com.kingdee.bos.framework.BOProxy.getProxy(plug
32、inCtx.getContext(),pk).callCmethod(methodSignature,args)服务端上下文(Context ctx )元数据 PK(IMetaDataPK pk)方法名称(String methodSignature)参数对象数组(Object args)参数获取方式参见客户端业务方法调用。2.2 快速插入业务方法调用可以通过动态扩展平台的扩展定义中的上下文页签中“调用自定义业务方法节点”快速生成调用业务方法模版脚本,如下图所示:双击这里自动生成模板脚本据全路径业务方法描述中拷贝过来据全路径com.kingdee.bos.framework.BOProxy.g
33、etProxy(pluginCtx.getContext(),pk).callCmethod(methodSignature,args)然后替换相应参数即可。小提示:使用快速插入业务方法调用方式,你不需要关心使用客户端方式还是服务端方式,平台会自动根据当前方法判断调用方式第三章 自定义异常3.1 定义自定义异常建立一个方案后,会自动创建一个异常,增加异常只需增加子异常即可。业务异常定义后,可以通过工具类抛出异常。在一个异常中子异常号不能重复。3.2 脚本中调用自定义异常在脚本中使用工具类 com.kingdee.eas.ep.plugin.ExceptionUtil 调用自定义异常,该类中将根
34、据异常名和子异常名获取异常并抛出.3.2.1 服务器端调用异常方式服务器端调用方式:com.kingdee.eas.ep.plugin.ExceptionUtil.throwOnServer(pluginCtx.getContext(),name,subName,args)其中参数为:Context ctx-服务器上下文String name-异常名称String subName-子异常名称Object args-参数对象数组,可删去argsi中的内容将会替代对应的 i中的内容,如果没有参数,可以省略该参数。3.2.2 客户端异常调用方式客户端调用方式:com.kingdee.eas.ep.p
35、lugin.ExceptionUtil.throwOnClient(name,subName,args)参数含义String name-异常名称String subName-子异常名称Object args-参数对象数组,可删去argsi中的内容将会替代对应的 i中的内容,如果没有参数,可以省略该参数。3.3 快速插入自定义异常调用可以通过动态扩展平台的扩展定义中的上下文页签中“异常工具”快速生成调用异常模版脚本,如下图所示:双击自动生成com.kingdee.eas.ep.plugin.ExceptionUtil. throwOnClient (name,subName,args),然后替换
36、相应参数即可。已 3.1 中新建异常为例,则替换为:com.kingdee.eas.ep.plugin.ExceptionUtil.throwOnClient(“TesttException“,“ssssd“,“参数 1“,“参数 2“); 运行效果如下图所示 。自动生成模板脚本据全路径双击据全路径注意一:异常信息中可以存在“ 数字”这种格式的内容,表示这部分需要在运行时替换为具体信息。比如异常定义为“测试异常;参数一:0,参数二1” ,调用该异常的时候,以数组形式传入参数:“参数 1“,“参数 2“第四章 自定义基础资料或单据动态扩展平台支持自定义基础资料和单据的实现原理是先通过内置的模板拷
37、贝生成新的动态元数据,然后通过模板这个壳来运行。新生成的元数据可进行业务调整。所有对动态内容的操作,最终都转换为对模板的操作,模板根据具体元数据的信息,进行相应的转换后,再执行操作。 4.1 获取自定义基础资料和单据远程接口首先新建自定义基础资料或单据,从模板复制,选择包名,输入名称,如:路径:com.kingdee.eas.basedata.assistant 名称:Termsofapply创建完成后,客户端调用新增、修改、查询等动作首先需要一个远程接口,可通过如下方式获得:var bizInterface = com.kingdee.bos.BOSObjectFactory.createR
38、emoteCommonBOSObject(new com.kingdee.bos.metadata.MetaDataPK(entityPK)其中 entityPK 为自定义基础资料的路径+”.app.”+包名(实体的全名称),如示例中则为:com.kingdee.eas.basedata.assistant.app.Termsofapply4.2 传入参数获取远程接口需要传入 entityPK,为自定义基础资料的实体的全名称,如” com.kingdee.eas.basedata.assistant.app.Termsofapply”。其也可在自定义基础资料实体元数据检查中获得,如下图打开元数
39、据检查选择新建基础资料或单据路径据全路径输入实体名称据全路径选择模板据全路径取其中 entityObject 属性获取实体全名。4.3 返回值类型返回值为 com.kingdee.eas.ep.IDataBaseCustom 类型,这是由于其从该模板生成所致。这样 IDataBaseCustom 上的所有操作,动态基础资料都有,也均可以通过该实例执行。元数据检查据全路径复制获取实体全路径据全路径4.4 示例脚本以新增操作为例,新增如下,首先获取 entityPK( 参见 4.2.) ,元数据查获取BOSType,如下图:/创建值对象function createNewData(entityPK
40、)var objectValue = new com.kingdee.eas.ep.DataBaseCustomInfo();var entityBosType = “9DD486DB“; /实体的 bosType,可通过 元数据检查获得objectValue.setBOSType(new com.kingdee.bos.util.BOSObjectType(entityBosType);/实体的 PK,可通过元数据检查 获得objectValue.setPK(new com.kingdee.bos.metadata.MetaDataPK(entityPK);return objectValu
41、e;var entityPK = “com.kingdee.eas.basedata.assistant.app.Termsofapply“;var object = createNewData(entityPK);/通过实体 PK 获取自定义基础资料或单据接口,所需参数为实体 pkvar bizInterface = com.kingdee.bos.BOSObjectFactory.createRemoteCommonBOSObject(new com.kingdee.bos.metadata.MetaDataPK(entityPK);bizInterface.addnew(object);
42、 /新增(com.kingdee.eas.ep.DataBaseCustomInfo)object).setName(“TestName”); object.setId(opk.getKeyValue(“id“);object.setName(“myName“);复制获取实体 bosType据全路径bizInterface.update(opk,object);/更新/更新值对象/需要更新记录的 id,实际调用过程可从参数等取得var fid = “87aUzEo7QWiREPo/LfoyDp3Uhts=“ var entityPK = “com.kingdee.eas.basedata.as
43、sistant.app.Termsofapply“;/通过实体 PK 获取自定义基础资料或单据接口,所需参数为实体 pkvar bizInterface = com.kingdee.bos.BOSObjectFactory.createRemoteCommonBOSObject(new com.kingdee.bos.metadata.MetaDataPK(entityPK);/通过 id(字符串型)创建 ObjectUuidPK 为更新方法所须参数类型var opk = new com.kingdee.bos.dao.ormapping.ObjectUuidPK(com.kingdee.bo
44、s.util.BOSUuid.read(fid);/取得原值对象var object = bizInterface.getValue(opk);/更改值对象某些属性object.setName(“myTestUpdateName“);/更新bizInterface.update(opk,object); /删除值对象var fid = “87aUzEo7QWiREPo/LfoyDp3Uhts=“/需要更新记录的 idvar entityPK = “com.kingdee.eas.basedata.assistant.app.Termsofapply“;var bizInterface = co
45、m.kingdee.bos.BOSObjectFactory.createRemoteCommonBOSObject(new com.kingdee.bos.metadata.MetaDataPK(entityPK);var opk = new com.kingdee.bos.dao.ormapping.ObjectUuidPK(com.kingdee.bos.util.BOSUuid.read(fid);bizInterface.deleteBatchData(opk);/删除注意: 值对象创建必须设置一个实体的 bosType 和 PK,引擎是通过这两项进行ORMMapping 的。删除、
46、更新、查看等动作由于值对象都存在 id,所以其可以自动解析获得,不需要手工设置 bosType 和 PK。对于删除操作,需要用 deleteBatchData,参数为ObjectUuidPK 数组。第五章 给常用控件添加事件5.1 F7给 F7 添加 DataChange 事件/F7 控件增加值改变监听pluginCtx.getKDBizPromptBox(“prmtHROrg“).addDataChangeListener(function (event)com.kingdee.eas.util.client.MsgBox.showInfo(“$PromptBox“);/ do someth
47、ing);5.2 CheckBoxwith(easNames)var chkIsCentralBalance = pluginCtx.getKDCheckBox(“chkIsCentralBalance“);chkIsCentralBalance.addChangeListener(function(event)com.kingdee.eas.util.client.MsgBox.showInfo(“$CheckBox “);/ do something); 5.3 ComboBox/选择事件with(easNames)var dataSourceCombo= pluginCtx.getKDC
48、omboBox (“dataSourceCombo“);dataSourceCombo. addItemListener (function(event,method)com.kingdee.eas.util.client.MsgBox.showInfo(“$ComboBox “);/ do something);5.4 Button/Button 点击事件var okkDButton= pluginCtx.getKDButton (“okkDButton“);okkDButton. addActionListener (function(event)com.kingdee.eas.util.
49、client.MsgBox.showInfo(“$Button“);/ do something); 5.5 TextFiled var myTextFiled = pluginCtx.getKDTextFiled (“myTextFiled“);myTextFiled.addActionListener(function(event)com.kingdee.eas.util.client.MsgBox.showInfo(“$myTextFiled “);/ do something); /添加焦点事件myTextFiled.addFocusListener(function(event, methodName)com.