页面树结构
转至元数据结尾
转至元数据起始

1  初级开发

1.1  前端脚本开发

1.1.1  脚本基础

        前端脚本是标准的JavaScript脚本,语法与ECMAScript 6相同,开发人员与实施人员需要一定的JavaScript基础。同时,前端脚本为配合DWF的UI表单的操作而编写,还需理解DWF的界面表单定制功能。

  下面介绍前端JS脚本常用的API。

1.1.1.1  获取表单界面各元素控件

//控件的id可在app端开发者工具中拾取,后期可在modeler中直接拷贝
var addinElement = this.getAddinById("指定控件的id");
//设置控件在界面中的错误信息提示
addinElement.setError("存在异常");
//$el为指定控件的dom元素,可以直接对dom元素修改其样式
addinElement.$el.style.display = "none";

1.1.1.2  修改表单界面元素控件的样式

//控件的 id 可在 app 端开发者工具中拾取,后期可在 modeler 中直接拷贝 
var addinElement = this.getAddinById("指定控件的 id");  
//$el 为指定控件的 dom 元素,设置该元素隐藏 
addinElement.$el.style.display = "none"; 
// 设置该元素重新显示
addinElement.$el.style.display = "block";

1.1.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.1.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.1.1.5.1  获取表格与表格中数据操作(获取与刷新)

//获取表格控件
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.1.1.5.2  表格头部生成与数据结构数据操作(增删改,浏览器内存实现,不涉及数据库)

//获取表格控件
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.1.1.6  业务对象的创建保存

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.1.1.7  业务对象的修改保存

var editObj = {id:'idXXX’,name:'nameXXX’};
var className = ‘Part’;
editObj.id = ‘idYYY’;
this.edit(editObj, className).then(rtnObj =>{   /*是否区分实体类与关联类?*/
   //返回的rtnObj为后台修改保存后返回的对象 
   //todo:针对rtnObj继续处理…
});

1.1.1.8  业务对象的删除

var curObj = {oid:uuid,id:’idXXX’,name:’nameXXX’};
var className = ‘Part’;
this.delete(curObj, className).then(rtnObj =>{
   // rtnObj返回值 = true/false,是否删除成功 
   //建议如果返回false,那么返回message错误原因提示信息 
});

1.1.1.9  业务对象查询(前端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.1.1.10  业务对象查询(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.1.1.11  打开表单前的数据初始化-实体类

//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.1.1.12  打开表单前的数据初始化(新建对象-典型场景)

//新建实体对象弹窗前处理
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.1.1.13  得到系统的上下文信息(当前登录用户与服务器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.1.1.14  获取实体类与关联类的元信息

//获取实体类的元模型信息
this.queryEntity(targetClassName).then(resMetaObj => {
   //在回调中处理resMetaObj值…
});
//获取关联类的元模型信息
this.queryRelation(targetClassName).then(resMetaObj => {
   //在回调中处理resMetaObj值…
});

1.1.1.15  调用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.1.1.16  Tab页签控件处理

var tabAddin = this.getAddinById("e09c6207-b578-4992-9f77-cbdc75bf86ce");
tabAddin.setDisable('子页签名称',true);      //禁用子页签
tabAddin.setDisable('子页签名称',false);     //取消子页签禁用
tabAddin.getSelectedTab();                   //获取当前选中子页bi
tabAddin.turnTo("作业卡下步骤");            //跳转激活子页签

1.1.1.17  表单中嵌套子表单的控制

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.1.1.18  设置单对象表单的(增改查)三态

//@扩展点: (暂不支持)
//在表单初始化事件中脚本处理表单的增删改三态(暂不可用,建议后期平台提供)
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.1.1.19  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.1.1.20  如何获取用户所属的用户组信息

var userId = this.store.state.user.userId;
var param = {…};
this.dwf_modeler_axios(`/dwf/v1/org/users/${userId}/groups`),param).then(groupOBJ => {
   // 在回调中处理groupOBJ值…
   //
});

1.1.1.21  如何通过脚本在新的标签打开界面

var targetClass = 'part';
var viewName = 'allPartList';
var args = {};  //args是{ conditionExpre: 查询条件, params: 初始化脚本 },如果没有args可以传空
this.openForm(targetClass, viewName, args);

1.1.1.22  在前端利用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.1.1.23  表单点选器控件与调用

//控件作用:获取指定类的表单模板下拉列表
//要获取表单模板的类名称(实体类或关联类)
var targetClass = "Part";
//多对象控件组中的'表单点选器'控件【内置的控件】
var addinElement = this.getAddinById("表单点选器控件ID");
//控件updateClass方法:刷新类的表单下拉列表
addinElement.updateClass(targetClass);

1.1.1.24  弹窗中确认按钮脚本终止弹窗

//...
return {error:'当前弹窗保存时发生错误,该Dialog窗口不能关闭!'} 
//...

1.1.1.24  Tree控件脚本

//获取树控件
var tree = this.getAddinById('TreeID'); 
//刷新整颗树
tree.updateTree();
//...

1.1.1.25 树表联动

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.1.1.25  Combox下拉列表控件脚本

//获取控件
var comB = this.getAddinById('uuid'); 
//设置控件值
comB.args.selectList = [{label:"label1", value:"value1"}, {label:"label2", value:"value2"}] ;
//...

1.1.1.26  表单点选器API

//获取控件
var formModelSelected = this.getAddinById('uuid'); 
var targetClass = 'classNameA';
formModelSelected.updateClass(targetClass);
//实体表映射-plt_mdl_metaclass

1.1.1.27 通过脚本下载附件

//直接弹框拿到指定oid对象附件属性,并弹窗
window.open("http://[服务器IP]:9090/dwf/v1/omf/classes/[实体类名]/objects/8572063499837340B8246FB37C596806/attributes/[附件属性名称]/bytes?attachment=true&0")
//不用弹框当前页面直接下载
const downloadFileA = document.createElement('a')
document.body.append(downloadFileA)
downloadFileA.href= "http://[服务器IP]:9090/dwf/v1/omf/classes/[实体类名称]/objects/8572063499837340B8246FB37C596806/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.1.1.28 创建前端内存对象

//新创对象的业务类名称
var className = "TeacherAuto";
//创建前端内存对象
var newBizOBJ = this.createObj(className);
//测试
console.log("新增内存对象oid=" + newBizOBJ.oid);

1.1.1.29 如何调用自定义控件读取后端数据显示在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.2  后端脚本开发

1.2.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)

1.2.2  脚本示例

1.2.2.1  启动Shell命令

//开始脚本编写
serverScript:  
//在/home/dwf目录中根据当前对象的oid和用户名创建一个文件
this.sh.execute("cd /home/dwf && touch " + _obj.oid + "-" + _env.userName);

1.2.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();

1.2.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();

1.2.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);


1.2.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");


1.2.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");
// 返回值是最终的存储到数据库的对象


1.2.2.7  删除实体类对象

this.omf.delete({oid: "5b68802859fc8da24c8da28be1640444c"}, "car");

1.2.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" );


1.2.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");


1.2.2.10  删除关联类对象

this.omf.delete({oid: "5b68802859fc8da24c8da28be1640444c"}, "carrel");

1.2.2.10  查询实体类和关联类对象

// 使用查询条件进行查询
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);

1.2.2.11 发出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);
}


1.2.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) 修改当前流程实例的流程变量值


1.2.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);


1.2.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);

1.2.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.3 应用场景脚本示例

1.3.1  应用场景介绍

  通过DWF的(数据、UI表单与功能等)建模功能,快速实现设备与工单管理的业务应用:
  1.设备的增删改查。
  2.设备维修工单的管理。

  实现的功能有:

  1.工单创建:工单编号、工单名称、维修设备编号、维修设备名称、工单工作详细描述、人员派工说明、作业零件工具说明、工单计划起止日期与工单计划工时等。
  创建保存:
  创建提交:工单状态由“创建中” 变更到”待派工”。

  2.人员派工:设定工单执行人(暂约定一人作业)、工单计划起止日期与工单计划工时等。
  派工提交:工单状态由“待派工” 变更到”已派工”。

  3.工单作业:
  (执行人)开始作业:修改工单“实际开始日期”属性,且工单状态由” 已派工” 变更到”作业中”。

  4.工单完成:
  (执行人)完成作业:填写工单作业日志与反馈说明、修改工单“实际完成日期”与”实际工时”属性,且工单状态由” 作业中” 变更到”已完成”。

  5.历史工单查询:针对“已完成“状态的工作进行查询与浏览。
  注:该界面可基于DWF模拟一下稍复杂页面的定义:表格 + 查询条件 + 表单 + 脚本(通过脚本实现查询条件、表格与表单的事件解释与联动)

1.3.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:{/*其它参数,可为空*/}
};
1.3.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("当前所选工单已完成提交,请处理后续的派工操作!");
}
1.3.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.3.5  功能界面: 工单派工

  注:执行角色约定“服务经理”。

1.3.6  界面操作脚本-提交
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.3.7  功能界面: 工单作业

  注:执行角色约定“服务工程师”。

1.3.8  开始作业-操作脚本
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("当前所选工单已开始作业,请处理后续的工单现场作业与完成作业操作!");
}
1.3.9  完成工单-操作脚本
debugger;
var curWO = this.obj;    //获取当前列表所选的对象(简介的写法)
if(!curWO){
    return null;
}
curWO.woState = '已完成';   //在完成工单弹窗前默认状态赋值
return{
    obj:curWO
}
1.3.10  功能界面: 历史工单查询

  注:执行角色约定“所有人员”。

1.3.11  查询操作脚本
	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("历史工单列表已刷新!");
	});
1.3.12  重置(查询)操作脚本
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("历史工单列表已刷新!");
});
1.3.13  表格行切换选择后刷新子属性卡表单
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 形式变量*/
});