初级开发
(一) 前端脚本开发
(1)脚本基础
前端脚本是标准的JavaScript脚本,语法与ECMAScript 6相同,开发人员与实施人员需要一定的JavaScript基础。同时,前端脚本为配合DWF的UI表单的操作而编写,还需理解DWF的界面表单定制功能。
下面介绍前端JS脚本常用的API。
(1-1)获取表单界面各元素控件
//控件的id可在app端开发者工具中拾取,后期可在modeler中直接拷贝 var addinElement = this.GetAddinById("指定控件的id"); //设置控件在界面中的错误信息提示 addinElement.setError("存在异常"); //$el为指定控件的dom元素,可以直接对dom元素修改其样式 addinElement.$el.style.display = "none";
(1-2)修改表单界面元素控件的样式
//控件的 id 可在 app 端开发者工具中拾取,后期可在 modeler 中直接拷贝 var addinElement = this.GetAddinById("指定控件的 id"); //$el 为指定控件的 dom 元素,设置该元素隐藏 addinElement.$el.style.display = "none"; // 设置该元素重新显示 addinElement.$el.style.display = "block";
(1-3)获取单对象表单中的对象
// obj为当前单对象(修改)表单绑定的业务对象 (且被表单修改后的数据) var curObj = this.obj; // 修改数据属性 curObj.wfstate = "已安装"; // 1-执行对象修改保存 this.editEObj({ className: this.className, obj: curObj }); // 2-edit=editEObj+ fresh前端;更适用于单对象表单的保存后UI刷新. this.edit(curObj, this.className);
(1-4)单业务类型(表单)表格操作
//前提说明:单对象表单表格,表示该表单中主要就一个表格控件,且绑定了表单对应的业务类型,同时打开该表单的操作动作也是list模式,在这种前提下可简化表格数据的操作处理 //获取当前表单中主表格中选中的(多)行数据 var selectedObjs = this.selectedObjs; if(selectedObjs && selectedObjs .length > 0){ selectedObjs.forEach(x => { //对选中的数据进行修改 x.assetState = "已安装"; // 对修改后的数据进行保存 this.editEObj({ className: this.className, obj: x }); }) }s //获取表格中所有的的行数据对象集合 var allRowDatas = this.getAll(); //TODO:…
(1-5)获取表格与表格中数据操作(获取与刷新)
//获取表格控件 var grdiAddin = this.GetAddinById("表格控件的id"); //获取表格中选中的(多)行数据 var selectedObjs = grdiAddin.getSelected(); if(selectedObjs && selectedObjs .length > 0){ selectedObjs.forEach(x => { //对选中的数据进行修改 x.assetState = "已安装"; // 对修改后的数据进行保存,此时优先editEObj而非edit this.editEObj({ className: this.className, obj: x }); }) } //获取表格中所有的的行数据对象集合 var allRowDatas = grdiAddin.getAll(); //处理表格数据的刷新 let query = `and obj.tyWorkOrderOid = '${this.obj.oid}'`; //表格后台数据强制刷新 this.handleQueryData({query:query,targetClass:"TyWork",fresh:true}).then(res=>{ //后台数据强查后,store会更新,会更新相同查询条件的多对象控件(表格) grdiAddin.freshData(query); //如果store有数据,就不进行后台数据查询,不然就会进行后台数据查询(可以理解为调用freshData后,一定至少发生过一次后台数据查询).这里在handleQueryData之后调用,所以不会进行后台数据查询. 用处是用指定查询的最新结果刷新表格 grdiAddin.fresh(); //不执行后台数据查询,从store中获取原查询条件的最新结果刷新表格. 区别在于: 1. 查询条件为原查询条件,不能指定新的查询条件; 2. 不进行后台数据查询 this.msgbox.success("表格已刷新!"); }) //表格行数据额外操作(如:删除一行) //grdiAddin.rowData.splice(selected[0].order,1); /*针对关联对象handleQueryData查询示例*/ var query = {query:`and obj.left_oid in (${conceptoidlists})`,type:'relation'}; his.handleQueryData({query:query, relationClass:"OntologyConcept2Attribute", fresh:true}).then( res=>{ //todo: });
(1-6) 表格头部生成与数据结构数据操作(增删改,浏览器内存实现,不涉及数据库)
//获取表格控件 var grdiAddin = this.GetAddinById("表格控件的id"); //通过getDefaultColumnDefs方法获取列定义对象 //通过传入一个对象进行个性化定制 var columnDefs1 = grdiAddin.getDefaultColumnDefs(); var columnDefs2 = grdiAddin.getDefaultColumnDefs(); ... var columnDefsn = grdiAddin.getDefaultColumnDefs(); //设置表头 grdiAddin.setColumnDefs([columnDefs1, columnDefs2, ..., columnDefsn]); var rowData = [ {field: 'field1', oid: 'oid1'}, {field: 'field2', oid: 'oid2'}, ... ] //设置表格数据 grdiAddin.setRowData(rowData); //添加表格数据 grdiAddin.setRowData(rowData, 'add'); //删除表格数据(参数对应行) grdiAddin.deleteRowData('1'); grdiAddin.deleteRowData(['2','3']); //修改表格数据(参数对应行) grdiAddin.updateRowData('4', {field: 'updatefield'});
(1-7)业务对象的创建保存
var newObj = {id:’idXXX’,name:’nameXXX’}; var className = ‘Part’; //新增保存 this.create(newObj, className).then(newRtnObj =>{ //返回的newRtnObj为后台新增后返回的对象,即包含了oid等属性 //todo:针对newRtnObj继续处理… }); this.create(newObj, className,{isRelation:false}/*如果关联类对象保存为true*/).then(newRtnObj =>{ //返回的newRtnObj为后台新增后返回的对象,即包含了oid等属性 //todo:针对newRtnObj继续处理… }); /*补充说明关联类对象保存,如下所示:*/ var linkOBJ = {left_属性名:属性值,right_属性名:属性值,relation_属性名:属性值}; /*需扁平化的关联类属性*/ this.create(linkOBJ, className,{leftClass:左类类名,rightClass:右类类名,relationClass:关联类名,isRelation:true}/*如果关联类对象保存为true*/).then(newRtnObj =>{ //返回的newRtnObj为后台新增后返回的对象,即包含了oid等属性 //todo:针对newRtnObj继续处理… });
(1-8)业务对象的修改保存
var editObj = {id:'idXXX’,name:'nameXXX’}; var className = ‘Part’; editObj.id = ‘idYYY’; this.edit(editObj, className).then(rtnObj =>{ /*是否区分实体类与关联类?*/ //返回的rtnObj为后台修改保存后返回的对象 //todo:针对rtnObj继续处理… });
(1-9)业务对象的删除
var curObj = {oid:uuid,id:’idXXX’,name:’nameXXX’}; var className = ‘Part’; this.delete(curObj, className).then(rtnObj =>{ // rtnObj返回值 = true/false,是否删除成功 //建议如果返回false,那么返回message错误原因提示信息 });
(1-10)业务对象查询(前端store中查询)
//查询条件 let queryConditon = { targetClass:"Part", query:{query:`and obj.id='${this.obj.name}'`}, fresh: true //是否从后台强制刷新查询(建议默认值为true) }; //该接口主要在store中进行查询(不查询后台DB) let queryResult = this.QueryResultAll(queryConditon); //TODO:查询结果queryResult处理
(1-11)业务对象查询(DB中查询)
//针对实体类查询 let queryConditon = { targetClass:"Part", query:{query:`and obj.id='${this.obj.name}'`}, fresh: true //是否从后台强制刷新查询(建议默认值为true) }; //基于DB进行查询 this.handleQueryData(queryConditon).then(res => { //针对res值(查询返回值)直接处理… }); //针对关联类查询 let queryPara = { relationClass:"OntologyConcept2Attribute", query:{query:`and obj.left_oid in (${conceptoidlists})`,type:'relation'}, fresh: true //是否从后台强制刷新查询(建议默认值为true) }; //基于DB进行查询 this.handleQueryData(queryPara).then(res => { //针对res值(查询返回值)直接处理… });
(1-12)打开表单前的数据初始化-实体类
//1-单打开前初始化脚本(所有参数说明) return { //对象对象 /*@扩展点:该处还需要考虑link关联类对象的json对象格式与表单初始化时针对该变量的解释,link对象的json格式参考如下: obj:{leftObj:{…},linkObj:{…},rightObj:{…}} 或 obj:{oid:’’,id:’’,name:’’, leftoid:’’, leftoid_id:’’, leftoid_name:’’,rightoid:’’, rightoid_id:’’, rightoid_name:’’} //上面两种格式可任取其中一种? */ obj:{…}, //单对象json //查询语句模式 query:`and obj.state='editing'` , //如果直接传空(``)那么表示全查 show:false, //是否显示,如果false渲染出来的是空白页面 data:{…} //是否有效待验证? }; //2-表单打开前初始化脚本(如根据当前所选对象打开弹窗查看详情) var curOBJ = this.selectedObjs[0]; return { obj: curOBJ, //单对象 data:{…} //补充参数对象,可空 }; //3-表单打开前初始化脚本(根据查询条件) return { query: `and obj.state='editing'`, //查询条件 data:{…} //补充参数对象,可空 }; //4-表单打开前初始化脚本(补充传递参数) return { data:{…} //补充参数对象,可空 }; //5-表单打开前初始化脚本(补充传递参数) var judgeRtn = false; //or true //TODO: judgeRtn赋值? return { if(judgeRtn){ show:true, //正常渲染表单界面元素 data:{…} //补充参数对象,可空 }else{ show:false //目前指不渲染表单界面元素 } };
(1-13)打开表单前的数据初始化(新建对象-典型场景)
//新建实体对象弹窗前处理 var judgeRtn = true; //TODO: judgeRtn值逻辑判断 var newObj= {}; //TODO: 给新对象赋初始值,建议前后端能提供API获取默认新内存对象(包括默认值处理),如:newObj = this.getDefaultNewOBJ(‘className’); newObj.state = ‘新建中’; return { if(judgeRtn){ obj: newObj, //正常渲染表单界面元素 data:{…} //补充参数对象,可空 }else{ show:false //不渲染表单界面元素 } }; //新建关联表单前初始化 //左对象保留字:left_[属性英文名],右对象保留字:right_[属性英文名],关联对象保留字:relation_[属性英文名] var grid = this.getAddinById(""); var rels = grid.getSelected(); if (rels.length > 0){ return{ obj:{ relation_leftOid: rels[0].relation_leftOid } } }
(1-14)得到系统的上下文信息(当前登录用户与服务器IP等)
//获取当前服务器端IP var curServerIP = this.env.serverIp; //获取当前登录用户 var curUserId = this.user.oid; //调用modeler端的restful API this.dwf_modeler_axios("/dwf/v1/org/users/${curUserId}/groups");
(1-15)获取实体类与关联类的元信息
//获取实体类的元模型信息 this.queryEntity(targetClassName).then(resMetaObj => { //在回调中处理resMetaObj值… }); //获取关联类的元模型信息 this.queryRelation(targetClassName).then(resMetaObj => { //在回调中处理resMetaObj值… });
(1-16)调用restful API
//1-调用modeler的restful API this.dwf_modeler_axios("/dwf/v1/org/users/${curUserId}/groups"),param).then(rtnOBJ => { // 在回调中处理rtnOBJ值… // }); //2-调用app的restful API示例1 //this.dwf_axios.post(`http://192.168.30.63/workorder/init`,param); let param = { }; this.dwf_axios.post(`/workorder/init?tyWorkOrderOid=${this.obj.oid}`,param).then(initRes => { //在回调中处理 if(initRes && initRes.data && initRes.data.result && initRes.data.result == true){ this.msgbox.success('已完成基于专家库作业信息的初始创建!'); }else if(initRes && initRes.data && initRes.data.msg){ alert(initRes.data.msg); } }); //2-调用app的restful API示例2 let param = [{ comment: "string", displayName: "测试创建用户", email: "string@fds.com", expiredTime: "2018-11-28T08:57:03.034Z", name: "testClientScriptHTTPRequest", password: "string" }] let res = this.dwf_axios.post(`org/users-create`,param).then(res => { if(res && res.返回值?){ alert("用户创建成功!"); }else{ alert("用户创建失败!"); } }); //3-调用其它服务器的restful API var param = {…}; this.axios.post(`uri全路径`,param).then(rtnOBJ => { //回调处理… });
(1-17)Tab页签控件处理
var tabAddin = this.GetAddinById("e09c6207-b578-4992-9f77-cbdc75bf86ce"); tabAddin.setDisable('子页签名称',true); //禁用子页签 tabAddin.setDisable('子页签名称',false); //取消子页签禁用 tabAddin.getSelectedTab(); //获取当前选中子页bi tabAddin.turnTo("作业卡下步骤"); //跳转激活子页签
(1-18)表单中嵌套子表单的控制
var subFormAddin = this.GetAddinById("表单控件ID");//获取子表单控件 subFormAddin.setDisplayType("visit"); var targetClass = ‘业务类型名称’; var viewName = ‘表单名称’; subFormAddin.updateShow(targetClass, viewName, { obj: {…}, //单业务对象 query: `and obj.oid = ${selectOBJOid} `, //查询条件,只能使用${变量名} data: {}, //其它扩展传递的参数 show: true, //控制子表单是否渲染显示 initScript: "", //初始化脚本 condition: " and obj.oid=$obj.oid " //可使用$obj,$env转义字符变量(在前端解释这些变量) });
(1-19)设置单对象表单的(增改查)三态
//@扩展点: (暂不支持) //在表单初始化事件中脚本处理表单的增删改三态(暂不可用,建议后期平台提供) var formType= this.data.formType; //data对象在表单打开前初始事件中赋值 this.t_create=false; this.t_edit=false; this.t_visit=false; if(formType == ‘create’){ this.t_create = true ; }else if(formType == ‘edit’){ this.t_edit = true; }else if(formType == ‘visit’){ this.t_visit = true; this.args.readonly = true; }
(1-20)DWF的几种msgbox用法
//目前如下几种消息提示都是在界面上方提示后自动关闭 this.msgbox.success("成功的提示"); this.msgbox.info("一般的提示"); this.msgbox.error("错误的提示"); //@扩展点(暂不支持) //建议补充模态弹窗提示后需手动关闭的msg提示框,参考如下: this.msgboxDialog.success(“title标题”,"成功的提示"); this.msgboxDialog.info(“title标题”,"一般的提示"); this.msgboxDialog.error(“title标题”,"错误的提示"); //@扩展点(暂不支持) //建议补充comfirm确认弹窗(包括确认与取消按钮) this.msgboxDialog.confirm (“title标题”,"请确认是否删除?", <function>(点击确定之后的回调函数), <function>(点击取消之后的回调函数));
(1-21)如何获取用户所属的用户组信息
var userId = this.store.state.user.userId; var param = {…}; this.dwf_modeler_axios(`/dwf/v1/org/users/${userId}/groups`),param).then(groupOBJ => { // 在回调中处理groupOBJ值… // });
(1-22)如何通过脚本在新的标签打开界面
var targetClass = 'part'; var viewName = 'allPartList'; var args = {}; //args是{ conditionExpre: 查询条件, params: 初始化脚本 },如果没有args可以传空 this.openForm(targetClass, viewName, args);
(1-23)在前端利用vueX保存自定义参数信息
//增加自定义参数 this.store.state.DWF_customer["testCustomerData"] = {"hello":"world"} {hello: "world"} //查询自定义参数 this.store.state.DWF_customer["testCustomerData"] {hello: "world"} //修改自定义参数 this.store.state.DWF_customer["testCustomerData"] = {"hello":"hello world"} {hello: "hello world"} //删除自定义参数 delete this.store.state.DWF_customer["testCustomerData"] //localStorage前端存储 localStorage.setItem("key",'value'); var data=localStorage.getItem("key"); localStorage.removeItem("key"); localStorage.clear();//清空localStorage中所有信息,慎用 //sessionStorage与localStorage类似,只是作用域小一些,推荐
(1-24)表单点选器控件与调用
//控件作用:获取指定类的表单模板下拉列表 //要获取表单模板的类名称(实体类或关联类) var targetClass = "Part"; //多对象控件组中的'表单点选器'控件【内置的控件】 var addinElement = this.GetAddinById("表单点选器控件ID"); //控件updateClass方法:刷新类的表单下拉列表 addinElement.updateClass(targetClass);
(1-25) 弹窗中确认按钮脚本终止弹窗
//... return {error:'当前弹窗保存时发生错误,该Dialog窗口不能关闭!'} //...
(1-26)Tree控件脚本
//获取树控件 var tree = this.getAddInByID('TreeID'); //刷新整颗树 tree.updateTree(); //...
(1-27)树表联动
var tree = this.getAddinById("BomTree"); var grid = this.getAddinById("MaterialList"); //获取当前被选中的树节点,树节点如果是根节点,则返回根节点的实体类对象,如果是中间节点则返回父亲到孩子的关联 var nodes = tree.getSelected(); if (nodes.length == 0) { return; } let query = ""; if ('right_oid' in nodes[0]){ //说明是孩子节点,表示是关联关系,其中右对象是选中对象,左对象是选中对象的父亲 query = `and leftclass.plt_oid = "${nodes[0].right_oid}"`; } else{ //说明是根节点 query = `and leftclass.plt_oid = "${nodes[0].oid}"`; } //要求表格刷新 grid.freshData(query);
(1-28)Combox下拉列表控件脚本
//获取控件 var comB = this.getAddInByID('uuid'); //设置控件值 comB.args.selectList = [{label:"label1", value:"value1"}, {label:"label2", value:"value2"}] ; //...
(1-29)表单点选器API
//获取控件 var formModelSelected = this.getAddInByID('uuid'); var targetClass = 'classNameA'; formModelSelected.updateClass(targetClass); //实体表映射-plt_mdl_metaclass
(1-30)通过脚本下载附件
//直接弹框拿到指定oid对象附件属性,并弹窗 window.open("http://[服务器IP]:9090/dwf/v1/omf/classes/[实体类名]/objects/[对象oid]/attributes/[附件属性名称]/bytes?attachment=true&0") //不用弹框当前页面直接下载 const downloadFileA = document.createElement('a') document.body.append(downloadFileA) downloadFileA.href= "http://[服务器IP]:9090/dwf/v1/omf/classes/[实体类名称]/objects/[对象oid]/attributes/[附件属性名称]/bytes?attachment=true&0" downloadFileA.click() document.body.removeChild(downloadFileA) //多附件下载补充说明 1-多附件的话,http://192.168.1.81:6060/dwf/v1/files-download/{fdsafds}, 大括号中是文件ID。文件ID则需要从对象的多附件属性当中取出来 2-对象中对应多附件控件的那个属性中存储的内容的格式是这样的:[{"name":"大数据时代.txt","size":"106.085KB","file_id":"ff8081816cedab93016cedc61a70004f"}]
(1-31)创建前端内存对象
//新创对象的业务类名称 var className = "TeacherAuto"; //创建前端内存对象 var newBizOBJ = this.createObj(className); //测试 console.log("新增内存对象oid=" + newBizOBJ.oid);
(1-32)如何调用自定义控件读取后端数据显示在Echart里面
//通过异步调动获取数据 this.handleQueryData({targetClass:"DataSource"}).then(res => { //将返回结果的特定属性提取出来 let dsNames = res.map( o => o.sourceName ); let dsTypes = res.map( o => o.sourceType ); //设置Echart的选项 option = { xAxis: { type: 'category', data: dsNames }, yAxis: { type: 'category' }, series: [{ type: 'line', data: dsTypes }] }; //myChart是约定的全局变量 myChart.setOption(option); })
(二)后端脚本开发
(1)脚本基础
后端脚本由服务端的JavaScript解释器解释执行,通过内置对象完成业务逻辑。默认的内置对象与解释如下:
1. this.obj :调用后端脚本的操作执行时,当前表单中填写的对象。
eg:this.obj.state
2. this.env:当前用户的登录信息。
eg: this.env.userName, this.env.userOid
3. this.className: 当前类。
4. this.em:hibernate 的EntityManager对象,可直接执行SQL。
eg:this.em.createNaitiveQuery(), this.em.createQuery()
5. this.sh:执行Shell脚本的对象。
eg:this.sh.execute(“…”)
6. this.logger:输出日志的对象。
eg:this.logger.info(“…”), this.logger.error(“…”)
7. this.restTemplate: 发送HTTP请求
8. this.omf: 修改OMF业务对象
9. console.log(...): 在服务端进程标准输出流打印内容
eg: console.log(obj)
(2)脚本示例
(2-1)启动Shell命令
//开始脚本编写 serverScript: //在/home/dwf目录中根据当前对象的oid和用户名创建一个文件 this.sh.execute("cd /home/dwf && touch " + _obj.oid + "-" + _env.userName);
(2-2) 直接执行SQL
//开始脚本编写 serverScript: //先定义一个函数,产生UUID function id() { return (((1+Math.random())*0x10000)|0).toString(16).substring(1); } //利用hibernate的em对象执行SQL this.em.createNativeQuery("insert into public.plt_org_user(plt_oid, plt_creator, plt_lastmodifier, plt_name, plt_displayname) values ('" + id() + "', '9C92E891E9AE534DB685737DE467A9D0', '9C92E891E9AE534DB685737DE467A9D0', 'testServerScript', '测试服务端脚本')").executeUpdate();
(2-3)执行存储过程
serverScript: /* 假设库中已经存在一个名为 test_procedure 的函数: CREATE OR REPLACE FUNCTION public.test_procedure() RETURNS void AS $BODY$ BEGIN insert into test_procedure_table (id, create_time) values (1, now()); END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100; ALTER FUNCTION public.test_procedure() OWNER TO postgres; 即,这个存储过程是向一个名为 test_procudure_table 的表中插入一条新数据 表 test_procudure_table 的定义: CREATE TABLE public.test_procedure_table ( id integer NOT NULL, create_time timestamp without time zone ) */ // 执行存储过程 this.em.createNativeQuery("select cast(test_procedure() as text)").getResultList();
(2-4)发送HTTP请求(restfulAPI调用)
/*1-Get请求,获取Json响应*/ this.logger.info("Get请求,获取Json响应"); var get_response = this.restTemplate.getForEntity("http://127.0.0.1:6060/test/get-admin-token" , java.util.Map.class); // 如果返回结果是json,response建议反序列化为Map,从而可以用.直接访问属性 var get_response_body = get_response.getBody(); this.logger.info("body: "+ get_response_body); // 访问Json中的属性 this.logger.info("body.message: " + get_response_body.message); /*2-Get请求,获取String响应*/ this.logger.info("Get请求,获取String响应"); get_response = this.restTemplate.getForEntity("http://www.baidu.com" , java.lang.String.class); // 如果返回结果不是json,response建议反序列化为String get_response_body = get_response.getBody(); this.logger.info("body: "+ get_response_body); /*3-Post请求,传递request body,获取json响应*/ var Map = Java.type('java.util.Map'); this.logger.info("Post请求,传递request body,获取json响应"); var request_body = {"name" : "hahahaname"}; var post_response = this.restTemplate.postForEntity("http://127.0.0.1:6060/dwf/v1/testPost", request_body, Map.class); var post_response_body = post_response.getBody(); this.logger.info("body: "+ post_response_body); /*4-Post请求,传递request body和http header,获取json响应*/ this.logger.info("Post请求,传递request body和http header,获取json响应"); // 引入Java中的相关类型 var HttpHeaders = Java.type("org.springframework.http.HttpHeaders"); var HttpEntity = Java.type('org.springframework.http.HttpEntity'); headers = new HttpHeaders(); headers.set("Authorization", "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU2ODAzNTcxOX0.5dfoAvd1SFQ7V_CnmzKMNbB7qR_k9Y1BK5zJXUuzc_BkhMFu9oEAVT3EUky1qhBlpihyPd7hV6136I1dnMX-GA"); request_body = { "appName": "test_json_engine", "extConfig": "string", }; request = new HttpEntity(request_body,headers); var Map = Java.type('java.util.Map'); post_response = this.restTemplate.postForEntity("http://127.0.0.1:6060/dwf/v1/apps-create", request, Map.class); post_response_body = post_response.getBody(); this.logger.info("body: "+ post_response_body);
(2-5)更新实体类对象
var newobj = this.omf.edit({ "creator": "9C92E891E9AE534DB685737DE467A9D0", "createTime": 1574322941000, "lastModifyTime": 1574322941000, "lastModifier": "9C92E891E9AE534DB685737DE467A9D0", "yzqplainref": "new_obj_3", "oid": "D883978E62398F4FB35F30A669B0E113", "id": "YZ20191121155541204", "currentProcess": "test_script1" }, "yzqright");
(2-6)创建实体类对象
var newobj = this.omf.create({ "creator": "9C92E891E9AE534DB685737DE467A9D0", "createTime": 1574322941000, "lastModifyTime": 1574322941000, "lastModifier": "9C92E891E9AE534DB685737DE467A9D0", "yzqplainref": "new_obj_3", "oid": "D883978E62398F4FB35F30A669B0E113", "id": "YZ20191121155541204", "currentProcess": "test_script_create" }, "yzqright"); // 返回值是最终的存储到数据库的对象
(2-7) 删除实体类对象
this.omf.delete({oid: "5b68802859fc8da24c8da28be1640444c"}, "car");
(2-8)更新关联类对象
serverScript: var newobj = this.omf.edit( { "creator": "9C92E891E9AE534DB685737DE467A9D0", "rightClass": "yzqright", "rightOid": "D883978E62398F4FB35F30A669B0E113", "oid": "988310EF59582C4E9DD11816DD0687CD", "version": 999, "leftOid": "2A47A5892C3C5746B0C063C72617E90F", "createTime": "1574322941000", "leftClass": "yzqleft", "multiFile": "[{\"name\":\"canlendar.png\",\"size\":\"4.863KB\",\"file_id\":\"ff8081816e8cc9d6016e8cf5a7cb0047\"}]" }, "yzqrelation" );
(2-9) 创建关联类对象
var newRelation = this.omf.create( { "creator": "9C92E891E9AE534DB685737DE467A9D0", "rightClass": "yzqright", "rightOid": "D883978E62398F4FB35F30A669B0E113", "version": 1024, "leftOid": "2A47A5892C3C5746B0C063C72617E90F", "createTime": "1574322941000", "leftClass": "yzqleft", "multiFile": "[{\"name\":\"canlendar.png\",\"size\":\"4.863KB\",\"file_id\":\"ff8081816e8cc9d6016e8cf5a7cb0047\"}]" }, "yzqrelation");
(2-10)删除关联类对象
this.omf.delete({oid: "5b68802859fc8da24c8da28be1640444c"}, "carrel");
(2-11)查询实体类和关联类对象
// 使用查询条件进行查询 var objs = this.omf.handleQueryData("and obj.version > 100 order by obj.version limit 5", "yzqrelation"); this.logger.info(JSON.stringify(objs)); // 使用oid进行查询 var obj = this.omf.getByOid("021C686BD26F594F9CA8AE56489BBEE7", "yzqrelation"); console.log(obj);
(2-12)发出POST请求百度AI,识别车型
使用百度人工智能开放平台,对实体类对象附加的图片进行分析,更新车型颜色信息。
https://ai.baidu.com/ai-doc/VEHICLE/tk3hb3eiv
注意:访问之前,需要申请百度AI应用。从而获得开发者专属的access_token。
// 识别车辆信息 // 详细说明见:https://ai.baidu.com/ai-doc/VEHICLE/tk3hb3eiv var r = carAi(this.obj.oid, 'Asset', 'assetImg'); // 根据情况更新对象,例如: this.obj.assetDesc = r.result[0].name //识别出车型信息 this.omf.edit(this.obj, this.className); // 也可以绘制图像 drawResult(this.obj.oid, 'Asset', 'assetImg'); // 返回识别结果 this.res = r.result[0].name + r.color_result //在指定属性上绘制图像 function drawResult(oid, targetClass, targetAttr, r){ var Color = Java.type('java.awt.Color'); var Font = Java.type('java.awt.Font'); var Graphics = Java.type('java.awt.Graphics'); var BasicStroke = Java.type('java.awt.BasicStroke'); var BufferedImage = Java.type('java.awt.image.BufferedImage'); var File = Java.type('java.io.File'); var ImageIO = Java.type('javax.imageio.ImageIO'); //绘制图像 img = null; f = null; path = this.omf.getFilePath(this.obj.oid, 'Asset', 'assetImg'); try { f = new File(path); img = ImageIO.read(f); } catch(e) { this.logger.error(e.toString()); } temp = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB); x = Math.round(r.location_result.left); y = Math.round(r.location_result.top); h = Math.round(r.location_result.width); w = Math.round(r.location_result.height); g = temp.getGraphics(); g.drawImage(img, 0, 0, null); g.setFont(new Font("Arial", Font.PLAIN, 80)); g.setColor(new Color(255, 0, 0, 255)); g.setStroke(new BasicStroke(3.0)); g.drawRect(x, y, h, w); g.dispose(); f = new File(path); try { ImageIO.write(temp, "png", f); } catch (e) { this.logger.error(e.toString()); } } function carAi(oid, targetClass, targetAttr){ // 引入SpringBoot中的一些关键类 var HttpHeaders = Java.type('org.springframework.http.HttpHeaders'); var HttpEntity = Java.type('org.springframework.http.HttpEntity'); var MediaType = Java.type('org.springframework.http.MediaType'); var StringHttpMessageConverter = Java.type('org.springframework.http.converter.StringHttpMessageConverter'); var StandardCharsets = Java.type('java.nio.charset.StandardCharsets'); var LinkedMultiValueMap = Java.type('org.springframework.util.LinkedMultiValueMap'); var JString = Java.type('java.lang.String'); // 将当前实体类中文件对象转变成Base64String,DWF内置调用 var imgStr = this.omf.getString(oid, targetClass, targetAttr); //替换成UTF-8编码 this.restTemplate.getMessageConverters() .add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8)); //必须设置请求头 headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); //设置请求体 var request_body= new LinkedMultiValueMap(); request_body.add("image", imgStr); request = new HttpEntity(request_body,headers); // access_token需要从百度人工智能平台申请 var baiduai = "https://aip.baidubce.com/rest/2.0/image-classify/v1/car?access_token=[自己申请的]"; post_response = this.restTemplate.postForEntity(baiduai, request, JString.class); var body = post_response.getBody(); this.logger.info(body); //处理返回结果 return JSON.parse(body); }
(3)工作流相关脚本
在流程模版编辑界面,可以绑定工作流的相关脚本,包括:开始节点的起始脚本(发起工作流实例后调用)、人工节点的前处理脚本(人工任务被激活后调用)和后处理脚本(提交人工任务时调用)、结束节点的结束脚本(流程被终止或完成时调用)。
除了在1.2.1中提到的默认的内置对象外,还包括工作流相关的内置对象:
1. this.wfProcess :调用后端脚本的操作执行时,当前工作流流程实例
eg:this.wfProcess.enClassInstanceId 当前流程实例所绑定的实体类对象oid
属性包括:id、name、author、authorId、releaser、releaserId、description、launchUserId 、launchUser 、launchTime、endTime、suspensionTime、updateTime、terminationTime、status(7已终止 8已完成)、targetClassName、targetObjectOId、proprietors办理人、copiers抄送人、invitations参与人、customData可定制内容
2. this.wfTask:当前活动节点实例,当调用起始脚本时为StartPointInstance类,当调用前/后脚本时为ManualTaskInstance类,当调用结束脚本时为EndPointInstance类
eg:this.wfTask.starTime 当前活动节点开始的时间
属性包括:id、name、enabledTime、startTime、endTime、outputs、inputs、accessibleVars、status、
formName/launchFormName/endFormName、startOperation/beforeOperation AfterOperation/endOperation
ManualTaskInstance:submitter、submitterId、comments、goAhead用户表单提交后是否提交任务、lastTaskId上一个任务实例的Id
3. this.wfEngine: 当前流程引擎实例。
eg:this.wfEngine.saveAccessibleDataVariable(taskId, userId, list) 修改当前流程实例的流程变量值
(3-1)(任务前处理脚本) 根据流程发起人修改当前人工节点所绑定的表单
var launchUser = this.wfProcess.getLaunchUser(); //获得流程发起人的DisplayName this.logger.info(launchUser); if(launchUser == "零件报销员1"){ this.wfTask.setFormName("applicationForm1");//设置为零件报销申请表1 }else if(launchUser == "零件报销员2"){ this.wfTask.setFormName("applicationForm2");//设置为零件报销申请表2 } this.logger.info(this.wfTask.getFormName);
(3-2) (任务前处理脚本) 根据上一个任务设置当前任务的办理人
var lastTaskId = this.wfTask.getLastTaskId(); //获得上一步任务的Id this.logger.info("lastTaskId:"+lastTaskId); var lastManualTaskInstance = this.wfEngine.getManualTaskInstanceById(lastTaskId); //获取上一步任务实例 var lastSubmitter = lastManualTaskInstance.submitter; this.logger.info("lastSubmitter:"+lastSubmitter); if(lastSubmitter == "零件报销员1"){ this.wfTask.setSubmitter("领导1");//设置为零件报销员1的领导 this.wfTask.setSubmitterId("00000000000000000000000000000Hgn");//设置为零件报销员1的领导的oid }else if(launchUser == "零件报销员2"){ this.wfTask.setFormName("领导2");//设置为零件报销员2的领导 this.wfTask.setSubmitterId("00000000000000000000000000000Hgn");//设置为零件报销员2的领导的oid } this.logger.info("this.wfTask.getFormName:"+this.wfTask.getFormName);
(3-3)(任务后处理脚本)任务完成之后将对象的属性更改为任务的名称
//this.obj代表流程中正在处理的对象 this.obj.woDesc = this.wfTask.name; //将需要更新数据库的对象转化为数组 var objs = [this.obj]; //this.wfProcess.targetClassName代表流程中的对象类名称 var newObjs = this.entityUpdateService.updateEntityObjs(this.wfProcess.targetClassName, objs);
(三)应用场景脚本示例
(1)应用场景介绍
通过DWF的(数据、UI表单与功能等)建模功能,快速实现设备与工单管理的业务应用:
1.设备的增删改查。
2.设备维修工单的管理。
实现的功能有:
1.工单创建:工单编号、工单名称、维修设备编号、维修设备名称、工单工作详细描述、人员派工说明、作业零件工具说明、工单计划起止日期与工单计划工时等。
创建保存:
创建提交:工单状态由“创建中” 变更到”待派工”。
2.人员派工:设定工单执行人(暂约定一人作业)、工单计划起止日期与工单计划工时等。
派工提交:工单状态由“待派工” 变更到”已派工”。
3.工单作业:
(执行人)开始作业:修改工单“实际开始日期”属性,且工单状态由” 已派工” 变更到”作业中”。
4.工单完成:
(执行人)完成作业:填写工单作业日志与反馈说明、修改工单“实际完成日期”与”实际工时”属性,且工单状态由” 作业中” 变更到”已完成”。
5.历史工单查询:针对“已完成“状态的工作进行查询与浏览。
注:该界面可基于DWF模拟一下稍复杂页面的定义:表格 + 查询条件 + 表单 + 脚本(通过脚本实现查询条件、表格与表单的事件解释与联动)
(2) 界面操作脚本-新增
let obj = { woId:'', name:'', eqOid:'', eqName:'', woDescription:'', dowDescription:'', toolDescription:'', planStartDate:null, planEndDate:null, planDuration:1.0, startDate:null, endDate:null, actualDurationNew:1.0, woState:'创建中', executorOid:'', executorId:'', executorName:'', excuteLog:'' } return { obj: obj, data:{/*其它参数,可为空*/} };
(3)界面操作脚本-提交
clientScript: debugger; var selectedWOObjs = this.selectedObjs; if(selectedWOObjs.length < 1){ this.msgbox.info("请选择工单后再提交!"); }else if(selectedWOObjs.length > 1){ this.msgbox.info("不能一次提交多个工单,请选择一个工单后再提交!"); }else{ //修改工单属性 var editOBJ = selectedWOObjs[0]; editOBJ.woState = '待派工'; //对象修改保存 this.edit(editOBJ, 'WorkOrder'); this.msgbox.success("当前所选工单已完成提交,请处理后续的派工操作!"); }
(4)界面操作脚本-刷新
clientScript: //获取表格,表格的ID目前可通过APP前端获取(在modeler中也可获取) var allRowDatas = this.selectedObjs; let query = ` and obj.woState = '创建中' `; //查询条件 //开始后台数据的查询 this.handleQueryData({query:query,targetClass:"WorkOrder",fresh:true}).then(res=>{ grid.freshData(query); //数据查询 this.msgbox.success("表格已刷新!"); });
功能界面: 工单派工
注:执行角色约定“服务经理”。
(1)界面操作脚本-提交
clientScript: debugger; var selectedWOObjs = this.selectedObjs; if(selectedWOObjs.length < 1){ this.msgbox.info("请选择工单后再提交!"); }else if(selectedWOObjs.length > 1){ this.msgbox.info("不能一次提交多个工单,请选择一个工单后再提交!"); }else{ //修改工单属性 var editOBJ = selectedWOObjs[0]; //弹窗确认提示 isConfirm = window.confirm("请确认该工单完成派工的提交操作?"); if(!isConfirm){ return; } if(editOBJ.woState == '待派工' && editOBJ.executorOid && editOBJ.executorOid != ''){ }else{ alert('当前工单不是[待派工]状态,或者没有选择工单执行人,不能执行提交操作!'); } editOBJ.woState = '已派工'; //对象属性保存 this.edit(editOBJ, 'WorkOrder'); this.msgbox.success("已完成派工提交!"); }
功能界面: 工单作业
注:执行角色约定“服务工程师”。
(1)开始作业-操作脚本
clientScript: debugger; var selectedWOObjs = this.selectedObjs; if(selectedWOObjs.length < 1){ this.msgbox.info("请选择工单后再开始作业!"); }else if(selectedWOObjs.length > 1){ this.msgbox.info("不能一次提交多个工单,请选择一个工单后再开始作业!"); }else{ //修改工单属性 isConfirm = window.confirm("确认自己开始该工单的作业处理?"); if(!isConfirm){ return; } var editOBJ = selectedWOObjs[0]; //开始工单作业的逻辑判断 if(editOBJ.woState == '已派工' && editOBJ.executorOid && editOBJ.executorOid != ''){ }else{ alert('当前工单不是[已派工]状态,或者没有选择工单执行人,不能开始作业操作!'); } editOBJ.woState = '作业中'; //修改工单状态 //editOBJ.startDate = $env.nowDate(); //类似变量 this.edit(editOBJ, 'WorkOrder'); //修改保存 //执行表单刷新 this.msgbox.success("当前所选工单已开始作业,请处理后续的工单现场作业与完成作业操作!"); }
(2)完成工单-操作脚本
debugger; var curWO = this.obj; //获取当前列表所选的对象(简介的写法) if(!curWO){ return null; } curWO.woState = '已完成'; //在完成工单弹窗前默认状态赋值 return{ obj:curWO }
功能界面: 历史工单查询
注:执行角色约定“所有人员”。
(1)查询操作脚本
clientScript: debugger; //获取并拼接查询条件 //工单编号 var querySQL = ` and plt_woState='已完成' ` ; var txt_GDBH = this.GetAddinById("ce1e78c5-5dfd-449c-97fc-1d761215f998"); try { txt_GDBH = txt_GDBH.getValue().replace(/^\s+|\s+$/g, ""); } catch (error) { txt_GDBH = ''; } if(txt_GDBH != ''){ querySQL += ` and plt_woId LIKE '%${txt_GDBH}%'`; } //工单名称 var txt_GDMC = this.GetAddinById("439b8e11-d35c-427f-a45d-63d7fa5b293a"); try { txt_GDMC = txt_GDMC.getValue().replace(/^\s+|\s+$/g, ""); } catch (error) { txt_GDMC = ''; } if(txt_GDMC != ''){ querySQL += ` and plt_name LIKE '%${txt_GDMC}%'`; } //设备编号 var txt_SBBH = this.GetAddinById("5ef0826b-d1a0-4bca-a645-f2d918e395de"); try { txt_SBBH = txt_SBBH.getValue().replace(/^\s+|\s+$/g, ""); } catch (error) { txt_SBBH = ''; } if(txt_SBBH != ''){ querySQL += ` and plt_eqId LIKE '%${txt_SBBH}%'`; } //设备名称 var txt_SBMC = this.GetAddinById("7b74e053-7ee9-432f-bac5-87e842325f91"); try { txt_SBMC = txt_SBMC.getValue().replace(/^\s+|\s+$/g, ""); } catch (error) { txt_SBMC = ''; } if(txt_SBMC != ''){ querySQL += ` and plt_eqName LIKE '%${txt_SBMC}%'`; } var query = querySQL + ` order by plt_woId `; //增加排序 var grid = this.GetAddinById('be59e6a9-0b85-4279-85fc-ce1b76f06d7e'); //获取表格 //后台数据刷新查询 this.handleQueryData({ "query": query, "targetClass": "WorkOrder", fresh: true }).then(res => { //表格数据刷新 grid.freshData(query); this.msgbox.success("历史工单列表已刷新!"); });
(2)重置(查询)操作脚本
clientScript: debugger; var grid = this.GetAddinById("be59e6a9-0b85-4279-85fc-ce1b76f06d7e"); //工单编号 var txt_GDBH = this.GetAddinById("ce1e78c5-5dfd-449c-97fc-1d761215f998"); try { txt_GDBH.setValue(''); } catch (error) { } //工单名称 var txt_GDMC = this.GetAddinById("439b8e11-d35c-427f-a45d-63d7fa5b293a"); try { txt_GDMC.setValue(''); } catch (error) { } //设备编号 var txt_SBBH = this.GetAddinById("5ef0826b-d1a0-4bca-a645-f2d918e395de"); try { txt_SBBH.setValue(''); } catch (error) { } //设备名称 var txt_SBMC = this.GetAddinById("7b74e053-7ee9-432f-bac5-87e842325f91"); try { txt_SBMC.setValue(''); } catch (error) { } //清空还原默认的查询条件 var query = ` and 1=1 and plt_woState='已完成' order by plt_woId `; //后台数据查询 this.handleQueryData({ "query": query, "targetClass": "WorkOrder", fresh: true }).then(res => { //表格数据刷新 grid.freshData(query); this.msgbox.success("历史工单列表已刷新!"); });
(3)表格行切换选择后刷新子属性卡表单
clientScript: //开始脚本编写 debugger; //获取内嵌的子表单容器控件 var detailForm = this.GetAddinById("4a609028-59f4-404c-8da2-8d12be161949"); //内嵌表单 var selectedWOObjs = this.selectedObjs; if(!selectedWOObjs || selectedWOObjs.length != 1){ detailForm.$el.style.display = 'block'; //block alert('不能获取到所选中的行数据!'); return; } //当前表格所选行对象 var curObj = selectedWOObjs[0]; //还原内嵌子表单的样式:即恢复显示 detailForm.$el.style.display = 'initial'; //block //刷新子表单的数据显示 detailForm.updateShow('WorkOrder', 'woForm', { obj: curObj, //query: "obj.tyWorkItemOid = '$(rowData.oid)'", /*查询条件方式获取子表单关联的业务对象*/ //data: {}, /*补充参数*/ show: true /*子表单可界面渲染显示,否则false不显示表单界面元素*/ //initScript: "", /*通过初始化脚本方式获取子表单关联的业务对象*/ //condition: "", /*通过查询条件方式获取子表单关联的业务对象,这种方式可使用到$obj 形式变量*/ });