多组织是指从不同业务管控纬度划分的不同的组织类型,比如采购组织、库存组织、销售组织等,不同的组织类型代表不同的统计和汇报纬度,最终形成一个矩阵式多维管控体系,简单理解可以将一个业务模块理解为一个组织类型,比如集中采购,集团层面建立一个"集采中心"组织,各分子公司下面分别建有采购部,但是所有分子公司的采购部归属于“集采中心”直接管控,如下图:
主视图:
采购组织视图:
平台的组织机构搭建时,主视图通过行政纬度进行显示,但是并不是展示的所有组织都是行政组织,可以分别切换不同的业务组织纬度显示组织架构。
数据权限的控制按照业务组织纬度管控,即平台提供按照业务组织进行数据隔离的控制,比如采购业务中的所有数据,只有采购组织中的人员才可以看到。
1、权限分配,在操作员维护中,可以指定所属组织及部门,同时可以设置可见范围,包括:当前组织、下级组织、当前部门、下级部门,凡是具有该组织或部门权限的人,都可以看到相应组织下的业务数据。
2、数据权限控制
a、数据模型:原则上来讲,所有业务单据,都有一个唯一所属的业务组织,在数据模型预置默认字段时,会默认增加 组织类型字段(注意需要指定默认组织编号,同时该字段应该是只读属性),在数据模型设计窗口选择“业务权限控制”选项,如果不勾选该选项,同时指定控制方式:按部门或按组织,则不进行数据权限控制,该设置在单据点上张、下张、前张、后张切换的时候有效。
b、数据列表:在数据列表定义中设定,需指定列表对应的组织编号字段、部门编号字段等属性
c、参照管理,在参照管理中设定,需指定列表对应的组织编号字段、部门编号字段等属性
d、自定义报表中使用方式
如果自己组织sql语句来查询形成一些个性化报表,可以直接通过联查权限表的方式进行控制,权限表说明如下:
bs_userauthm(权限主表)
字段名 | 字段描述 | 说明 |
---|---|---|
cusercode | 用户编号 | 实际的操作员编号 |
corgtype | 组织类型 | 比如采购组织编号或库存组织编号 |
cauthtype | 控制类型 | dep:部门权限 org 组织权限 |
bs_userauthc(权限子表)
字段名 | 字段描述 | 说明 |
---|---|---|
bs_userauthc_id | 关联主表外键 | |
corgid | 组织/部门ID | |
corgcode | 组织/部门编号 |
重新计算所有人的组织权限,一般在组织机构发生调整后进行重计算
this.resetAuthData(function(res){
})
form窗体中,所有数据主要存储在如下几个变量中:
1、this.form.data:存储单据的表头、表体数据,数据列表数据
2、this.form.remotedata: 存储Ajax类型数据集、脚本类型数据集、自定义查询数据集
3、this.form.filterdata: 存储查询条件数据(控件属性中,选中“查询条件”项的控件数据)
4、this.form.formpara: 存储地址参数信息、传入的窗体变量信息以及其他窗体自动生成的临时数据
5、this.form.voucherinfo:单据的相关元数据信息
xthis.form.userdata //userdata为全局变量容器
this.form.userdata.price = 100;//定义一个名字为price的全局变量
let iprice = this.form.userdata.price; //从全局变量中取出price变量
全局变量适用于存储不同方法或事件中共享的数据
使用全局变量需要注意在form数据切换的时候状态恢复,如表单前张后张切换的时候,应该将全局变量清空或者重新初始化,放置发生计算混乱。
数据精度定义位置: 控制面板 -- 系统设置 -- 精度定义,精度定义中可以自定义比如数量、单价、金额等相关数字类型的数据精度。
如何使用精度定义:
1、数据模型定义中,有“精度类型”列,如果字段为数字类型,从该列下拉项中可以选择已经定义好的数据精度,选择后“小数位数”列会自动带出对应的精度值
2、在窗体函数中,可以通过函数 this.form.getPrecision("精度符号") 获取到对应精度值,可通过 this.formatNumber(数值,精度) 实现四舍五入。
示例
xxxxxxxxxx
let inum = 232.5367
let iprecision = this.form.getPrecision("quan");//例如返回值2,代表保留2位小数
let inum1 = this.formatNumber(inum,iprecision) ;//结果为232.54 四舍五入,保留2位小数
注意点:
1、精度定义保存时,系统会自动更新“数据模型”中该精度对应所有字段的“小数位数”值
2、窗体中表头字段如果绑定的为数据模型中的数字类型字段,其精度会强制跟数据模型精度保持一致,前端设置无效
3、表格数据源如果为数据模型或者数据列表,其数字类型的精度也会强制跟数据模型保持一致,前端修改无效
xxxxxxxxxx
this.form.showDialog(
{
name:'FormID',
fullscreen:false,
appwindow:true,
reload:false,
title:"测试",
width:'60%',
height:'500px',
id:"form1",
src:"",
closed:function(){},
beforeClose:function(){
return true //允许关闭
},
formpara:{
para1:'a',
para2:'b',
filter:{
表格名称(或列表编号):{
lock:'',
条件编号1:''//查询条件默认值
}
}
}
})
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
name | 窗体FormID | 是 | 空 |
type | 类型 form(form窗体)或者portal(数据中心) | 否 | form |
fullscreen | 是否全屏 | 否 | false |
appwindow | app中打开新的app窗口,而不是div弹窗,子窗体通过 this.form.callParent 函数来调用父窗体方法 | 否 | false |
title | 窗体标题 | 是 | 空 |
width | 百分比或者实际宽度(如500px) | 是 | 空 |
height | 实际高度,如500px | 是 | 空 |
reload | 是否重新加载(如果重复打开同一个窗体,只有第一次才会初始化弹窗,后续再打开默认显示上一次打开的结果,如果需要重新初始化,则需要将此参数置为true | 否 | false |
id | 新窗口id,窗口唯一标识,如果id为一个已有值,则会打开之前窗口 | 是 | 空 |
para | 传递参数,新窗体中通过 [this.form.formpara.参数名] 来访问, 其中filter参数为系统预制参数,可以根据需要使用,起作用是在窗口加载数据时,设定表格数据源的查询条件,格式为:{表格1名称:{lock:’’,f1:’’},表格2名称:{lock:’’,f1:’’}},lock代表固定条件,f1代表列表定义中定义的查询条件编号 filter参数应用场景: 1、参照窗口打开前传入一段查询条件 2、弹窗打开列表显示时,传入一段查询条件或者给查询条件初始化一个默认值。 | 否 | {} |
closed | 窗口关闭后事件 | 否 | {} |
beforeClose | 窗口关闭前事件 | 否 | {} |
src | 如果打开的为非Vue页面,直接传入相对或完整网址,弹窗中将以内嵌iframe形式显示页面内容 |
xxxxxxxxxx
this.form.close() //弹出窗口中点击按钮关闭
this.form.parent.closeDialog(id) //直接调用父窗体关闭方法
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
id | 窗体ID | 是 | 空 |
xxxxxxxxxx
this.form.openDrawer(
{
name:"",//窗体ID
direction: "rtl",//可选值:rtl / ltr / ttb / btt
modal: true,//遮罩
showclose: true,//显示关闭按钮
size: "40%",//大小,百分比或者具体数值,如 300 或 30%
title: "",
autoclose: true,//点击空白自动关闭
reload: true,//窗口内容每次打开自动刷新
showheader: true,//显示标题
beforeClose:function(id){
//关闭前事件
return true;
}
})
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
name | 窗体FormID | 是 | 空 |
type | 打开类型:form(form窗体)或者portal(数据中心) | 否 | form |
direction | 打开的方向 rtl / ltr / ttb / btt | 是 | 空 |
size | 百分比或者实际宽度(如500) | 是 | 空 |
modal | 是否需要遮罩层 | 是 | 空 |
reload | 是否重新加载(如果重复打开同一个窗体,只有第一次才会初始化弹窗,后续再打开默认显示上一次打开的结果,如果需要重新初始化,则需要将此参数置为true | 否 | false |
showheader | 显示标题 | 是 | 空 |
showclose | 显示关闭按钮 | 否 | {} |
autoclose | 点击空白处自动关闭 | 否 | {} |
beforeClose | 窗口关闭前事件 | 否 | {} |
xxxxxxxxxx
this.form.closeDrawer()
窗体中需具有显示块容器,并且显示块容器属性中 指定了“弹出方式”
xxxxxxxxxx
this.form.showPanel("显示块名称",true/false)
子窗体是指通过弹窗打开的窗体,如果子窗体需要调用父窗体函数,使用 this.form.callParent(method,params)
注意:为了降低窗体间的耦合,建议使用此方法,这个方法在APP原生和H5中都可以使用,而不是直接使用this.form.parent.方法名 来调用
参数说明:
参数 | 必填 | 说明 |
---|---|---|
method | 是 | 父窗体方法名称 |
params | 否 | 方法参数,数组或者单独一个值,详见下方说明 |
params详细说明:
1、直接写为数组,这是比较通用的做法,如果父窗体方法为多个参数,比如有两个参数:code,name ; 那么params可写为 ["001","张三"]或者 ["001",{name:"张三"}],具体数组中每一项需要用什么类型,根据父窗体方法类型来匹配
2、如果父窗体方法只有一个参数,可以直接用一个值代替,比如父窗体需要一个字符串参数code,那么params直接写“001”也可以
示例
xxxxxxxxxx
//父窗体方法,code name 均为字符串类型
function doTest(code,name){
}
//子窗体调用
this.form.callParent("doTest",["001","张三"])
//父窗体方法,name为字符串,data为对象
function setData(name,data){
this.form.data.ccode = name;
this.form.data.cmemto = data.memo
this.form.data.cmaker = data.maker
}
//子窗体调用
this.form.callParent("setData",["001",{memo:"备注",maker:"张三"}])
//父窗体方法只有一个参数
function doTest(code){
}
//子窗体调用
this.form.callParent("doTest","001") //或者 this.form.callParent("doTest",["001"])
xxxxxxxxxx
let myform = this.form.getDialogForm(name)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
name | 窗体ID | 是 | 空 |
通过myform.data来访问弹出窗口中的数据集合
注意:本函数必须在父窗体才能够获得
APP原生应用无效
APP原生无效
xxxxxxxxxx
this.form.showDialog({
name:"参照窗体formid",
fullscreen:false,
title:"窗口标题",
width:'60%',
height:'500px',
id:"窗体id(唯一性)",
formpara:{
bhead:true,
refervue:this,
/*bhead为false有效
gridname:"",//表体参照时,表格名字
griddata:[],//表体数据源数组数据
rowindex:1,//数组序号
*/
reffield:参照数据源字段集合(数组),
retfield:返回字段集合(数组),
filter:{
表格名称:{lock:"",default:""}
}
}
})
formpara参数说明
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
bhead | 是否表头字段 | 是 | |
reffield | 参照返回字段集合,如[“cdepcode”,”cdepname“”] | 否 | false |
retfield | 回写字段集合,即表头需要参照回写的字段,如 [“depcode”,”depname”],跟reffield中的字段需要一一对应 | 是 | |
filter | 参照前需要传入的查询条件,如 filer:{“oa_depgrid”:{lock:”and cdepcode=‘01’”,default:“”}},lock表示固定条件,参照窗口中自定义过滤时都会自动带入lock条件,default为第一次加载数据的默认条件,参照窗口中二次过滤时会失效 | 否 |
说明
参照窗体界面可以自定义添加查询条件,有两种方式
1、窗体中拖入文本框等组件,“查询条件”属性打钩,绑定字段名手动修改为参照数据源中存在的字段名即可,需要指出的是如果该字段为文本类型,则是模糊匹配,如果是数字、日期类型,则条件为相等
2、点击查询按钮时手动调用刷新列表函数或者通过实现快速过滤组件或者查询按钮的点击事件,自己组织好查询条件传入 where 参数
xxxxxxxxxx
this.form.formpara //当前Form窗体中传递过来的变量对象,变量一般通过showDialog或者地址传值两种方式传入
获取当前页面地址参数集合:this.form.formpara.urlquery ,如 show?formid=123&code=001,分别通过this.form.formpara.urlquery.formid 和 this.form.formpara.urlquery.code 来获取两个参数值
重要更新:所有地址栏参数都可以通过 this.form.formpara[参数名]来获取
获取showDialog方式传递参数集合:参考showDialog中的formpara结构
xxxxxxxxxx
this.form.showFlowMap(flowcode,key,voucherno,formid);//打开流程视图
参数说明:
参数 | 说明 | 允许空 |
---|---|---|
flowcode | 流程编号 | 否 |
key | 单据ID | 是,为空时默认取当前单据的ID(当前窗体需为单据窗体有效) |
voucherno | 数据模型编号 | 是,为空时默认取当前单据的数据模型变化(当前窗体需为单据窗体有效) |
formid | 窗体ID | 是,为空时默认取当前窗体的formid(当前窗体需为单据窗体有效) |
系统会按照 voucherno和formid从流程图中寻找入口节点,如果找不到入口节点会报错
APP原生无效
xxxxxxxxxx
this.form.batchImport(vo,flag,pagesize) //当前Form窗体中必须有数据模型才可以使用此方法
参数说明
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
flag | 匹配标识,比如按照客户编号标识excel字段和单据字段的对应关系 | 否 | |
vo | 单据类型 | 否 | |
pagesize | 单页条数 | 否 | 50 |
导入完成后,数据自动保存至数据库
说明 如果调整导入模板的列是否显示,请在老版单据中设置字段的隐藏显示
APP原生无效
xxxxxxxxxx
this.form.doImport(function(data){},autocheck);//当前Form窗体中必须有数据模型才可以使用此方法
导入完成后,数据自动加载到表体,单据保存后,才最终保存到数据库。
参数说明
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
callback | 导入表体前回调方法,参数为data,格式为:{表体1别名:{字段1:值1,字段2:值2},表体2别名:{字段1:值1,字段2:值2}} | 否 | |
autocheck | 是否自动导入时校验,0或1 | 否 |
xxxxxxxxxx
this.confirm(msg, yescb, nocb);//当前Form窗体中必须有数据模型才可以使用此方法
参数说明
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
msg | 提醒内容 | 是 | |
yescb | 选择是对应的回调函数 | 是 | |
nocb | 选择否对应的回调函数 | 是 | |
示例
xxxxxxxxxx
this.confirm("确定要删除吗?",function(){
alert("删除成功")
},function(){
alert("取消删除")
})
通过菜单编号来检查当前用户是否具有该菜单权限
xxxxxxxxxx
this.menuAuth(menucode,callback,errcallback)
//示例
this.menuAuth("5001260209",function(menu){
//menu结构:{title:"采购申请",url:"focus/go.htm#/show?formid=88a1a630-1b51-4785-a051-4715beba4ea2"}
//回调函数中通过弹窗或新的浏览器窗口打开功能地址
},function(res){
//错误回调函数,比如权限验证失败或者异常
})
APP原生无效
公式定义窗口包括左侧的字段列表、函数列表,中间部分的公式编写窗口以及右侧的参数列表,通过js编写计算逻辑,最终结果通过return 的形式返回计算结果
xxxxxxxxxx
//打开公式编写窗口
let cscript = "";
this.form.openFormulaSet({
readOnly: false,//只读属性
para: [{name:"dbegin",des:"开始日期"}, {name:"dend",des:"截止日期"},{name:"itemcode",des:"项目编号"}],//参数列表
fields: //字段集合
[
/*通过模块区分字段集合,模块编号以字母开头*/
{ modulecode: "per", modulename: "人员信息", fields: [{ field: "ccode", title: "单据编号" }, { field: "imoney", title: "金额" }] },
{ modulecode: "dep", modulename: "部门信息", fields: [{ field: "ccode", title: "单据编号" }, { field: "imoney", title: "金额" }] },
],
scriptdata: cscript //js公式内容
}, function (data) {
//最终公式的js内容回写到相应字段
console.log(data)
/* 表体赋值
row.cscript = data;
row["sys_focusflag"] = "1"
*/
/*表头赋值
_this.form.data.cscript = data;
*/
});
如何后台执行公式?
公式执行需要通过自定义构件来调用,步骤如下:
1、新建类库项目
2、添加dll引用
标准版:CommonClass.dll , DataCommon.dll , Focus.log.dll , Focus.Formula.dll
微云版:Focus.Common.dll,Focus.DBFactory.dll,Focus.Logger.dll,Focus.Formula.dll
微云版注意事项:为保证运行时环境相关dll资源的正确使用,建议通过平台的扩展插件工程 Focus.UserPlugins进行自定义构件扩展,下载链接:
http://jh.focusinsoft.com/Focus.UserPlugins.rar
3、编写调用代码,如下:
xxxxxxxxxx
ComputManager manager = new ComputManager(_cmd);//实例化公式计算管理类
manager.CreateEngine();//初始化计算引擎
List<FormulaData> arrdata = new List<FormulaData>();//组织公式数据源
Dictionary<string, object> dic = new Dictionary<string, object>();
dic.Add("ccode", "0011");
dic.Add("imoney", 100);
arrdata.Add(new FormulaData { VariableName = "per", Data = dic });//VariableName对应字段集合中的模块编号
Dictionary<string, object> dic1 = new Dictionary<string, object>();
dic1.Add("ccode", "001");
dic1.Add("imoney", 150);
arrdata.Add(new FormulaData { VariableName = "dep", Data = dic1 });//VariableName对应字段集合中的模块编号
Dictionary<string, object> para = new Dictionary<string, object>();//组织公式参数
string paravalue = manager.ExcuteFormula(a.cvalscript, arrdata, para);//执行公式,并返回计算结果
manager.CloseEngine();//关闭计算引擎
Console.WriteLine(paravalue);//打印计算结果
说明:
1、公式数据源是指公式计算所使用到的数据集,比如示例代码中的 arrdata,是一个List类型的集合,FormulaData中VariableName为数据集名称(要以字母开头),Data为结果集,一般可用DataTable或Dictionary类型来传递;公式代码中通过 [数据集名称].[变量名] 就可以访问到数据集字段信息,比如DataTable实际运行是是一个Json数组,Dictionary是一个Json对象。
2、公式参数是指执行公式时,从外部传入的参数,必须一些条件参数,参数是一个Dictionary对象,实际运行时是一个Json对象,通过 params.[变量名] 即可获取变量值
3、公式执行中添加日志监控,参考 【插件管理-脚本日志】章节。
同步方式:this.doResponse(rescode,params,callback)
异步方式:this.doResponseAsync(rescode,params)
xxxxxxxxxx
this.doResponse(rescode,params,callback)
//示例
this.doResponse("opport01",{keyid:this.form.data.keyid},function(ret){
let str={}
eval("str="+ret);
if (str.bsuc==true){
alert("成功!")
}else{
this.msg(str.msg)
}
},function(){
//请求错误处理回调函数
})
//异步变同步方式
var ret = await this.doResponseAsync("opport01",{keyid:this.form.data.keyid})
console.log(ret.data)
参数说明
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
rescode | 响应编号 | 是 | |
params | 格式为{para1:value1,para2:value2},无参数则写{} | 是 | |
callback | 导入表体前回调方法,参数为data | 否 | |
xxxxxxxxxx
this.form.loadListData(gridname,{start:1,bsearch:0,where:''},cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | grid名称 | 是 | 空 |
start | 起始页号 | 否 | 1 |
bsearch | 是否重新查询,单据分页时,默认从服务器端获取缓存的数据,如果需要重新查询数据库,则设为1 可选值:0、1 | 否 | 0 |
where | 本次查询条件sql | 否 | 空 |
lockwhere | 固定查询条件,一次传递一直生效,如 and cdepcode=‘0101’ | ||
cb | 回调函数,Grid列表数据加载完成后的回调函数 | 否 | 空 |
刷新后,会自动更新关联的控件数据显示
说明:如果表格数据源为参照类型数据源,参照显示窗体中如果需要添加自定义查询条件,有两种方式
1、窗体中拖入文本框等组件,“查询条件”属性打钩,绑定字段名手动修改为参照数据源中存在的字段名即可,需要指出的是如果该字段为文本类型,则是模糊匹配,如果是数字、日期类型,则条件为相等
2、点击查询按钮时手动调用刷新列表函数或者通过实现快速过滤组件或者查询按钮的点击事件,自己组织好查询条件传入 where 参数
xxxxxxxxxx
this.form.loadAjaxData(dscode,para,cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
dscode | 数据集编号 | 是 | 空 |
para | 数据集参数,其格式为数据集定义中对应的ajax参数 | 否 | {} |
cb | 回调函数,参数为 data | 否 |
刷新后,会自动更新关联的控件数据显示
xxxxxxxxxx
this.form.loadScriptData(dscode,para,cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
dscode | 数据集编号 | 是 | 空 |
para | 数据集参数,其格式为数据集定义中对应的脚本函数参数 | 否 | {} |
cb | 回调函数,参数为data | 否 | |
刷新后,会自动更新关联的控件数据显示
xxxxxxxxxx
this.form.loadUserDefData(dscode,para,cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
dscode | 数据集编号 | 是 | 空 |
para | 数据集参数,其格式为数据集定义中对应的脚本函数参数 | 否 | {} |
cb | 回调函数,参数为data | 否 | |
刷新后,会自动更新关联的控件数据显示
xxxxxxxxxx
this.form.loadMasterData(dscode,para,cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
dscode | 数据集编号 | 是 | 空 |
para | 数据集参数,其格式为数据集定义中对应的脚本函数参数 | 否 | {} |
cb | 回调函数,参数为data | 否 | |
刷新后,会自动更新关联的控件数据显示
xxxxxxxxxx
this.callWebApi(para,cb);//回调方式
this.callWebApiAsync(para);//异步方式
para结构
参数名 | 参数说明 | 可空 | 补充说明 |
---|---|---|---|
method | 请求方式:post/get/delete/patch | 否 | |
posttype | 参数提交方式:form/body | 否 | |
url | 请求地址 | 否 | |
formdata | webapi 以提交形式传递的参数,为json对象,如:{code:"001",name:"张三"} | 是 | post参数 ,对于method为delete或patch,如果提交方式为form,会转化为地址栏参数 |
urlpara | webapi 以url形式传递的参数,为json对象,如:{code:"a"} | 是 | 地址栏参数 |
header | webapi header中需要传递的参数,为json对象,如:{code:"a"} | 是 | 一般传递token数据 |
返回值:
1、如果正确请求,结果即为web api的返回结果
2、如果请求发生异常,结果为json对象,格式:{bsuc:true/false,msg:"异常描述"}
示例:
xxxxxxxxxx
回调类型
this.form.callWebApi(
{
method: "post", posttype: "form", url: "http://localhost:8877/home/test_post", formdata: {
flowcode: "aaa11"
},urlpara:{para:"p1"}
},function(data){
console.log(data);//api返回结果
}
)
//异步方式
let ret = this.form.callWebApiAsync(
{
method: "post", posttype: "form", url: "http://localhost:8877/home/test_post", formdata: {
flowcode: "aaa11"
},urlpara:{para:"p1"}
}
)
console.log(ret);//api返回结果
xxxxxxxxxx
this.getRemotData(url,method,para,cb,errcb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
url | 请求地址 | 是 | |
method | 请求方式 get 或 post | 是 | |
para | 请求参数,如{code:"001",name:"002"} | 是 | |
cb | 回调函数,参数为data | 否 | |
errcb | 请求错误回调函数 | 否 | |
xxxxxxxxxx
this.getRemotDataAsync(url,method,para)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
url | 请求地址 | 是 | |
method | 请求方式 get 或 post | 是 | |
para | 请求参数,如{code:"001",name:"002"} | 是 | |
xxxxxxxxxx
//必须在async函数内才能使用同步请求
let data= await this.getRemotDataAsync("url","post",{code:"001"})
console.log(data)
xxxxxxxxxx
this.form.data
数据结构:{字段名1:”值”,字段名2:”值”,字段名3:”值”,字段名4:”值”,字段名5:”值”}
示例:获取表头部门编号字段(字段名:cdepcode)数据:
xxxxxxxxxx
var cdepcode = this.form.data.cdepcode //取值
this.form.data.cdepcode = "0101" //赋值,控件值会自动刷新
xxxxxxxxxx
this.form.data.列表类型或参照类型
数据结构:[{字段1:值,字段2:值,字段3:值,字段4:值},{字段1:值,字段2:值,字段3:值,字段4:值},{字段1:值,字段2:值,字段3:值,字段4:值}...]
此数据结构为一个数组
示例:数据源001是一个数据列表,列表类型为deplist
xxxxxxxxxx
this.form.data.deplist.length //总行数
this.form.data.deplist[0].cdepcode //第一行部门编号取值
this.form.data.deplist[0].cdepcode = "0101" //第一行部门编号赋值
xxxxxxxxxx
(async) this.form.addRow(gridname,rowcount,startindex,cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | 空 |
rowcount | 新增行数 | 否 | 1 |
startindex | 插入行位置 | 否 | null |
cb | 回调函数,如function(arr){ console.log(arr) } 通过回调函数可以自定义处理新增行的默认值 | 否 | null |
增行后如果在回调函数中进行表体行赋值操作,需要设定 “sys_focusflag” 字段,1代表行数据发生变动,0代表未变动,保存时只会处理发生变动的行。如
xxxxxxxxxx
this.form.addRow(“mytb”,10,0,function(arr){
for(let i=0;i<arr.length;i++){
arr[i]["cinvcode"] = "001";
arr[i]["iquantity"] = 100;
arr[i]["sys_focusflag"] = "1";//注意赋值,1代表行数据发生变动,0代表未变动,保存时只会处理发生变动的行
}
})
注:增行回调函数赋值中,如果给参照关联字段赋值,系统会自动带出其他参照相关字段,无需手动赋值。
xxxxxxxxxx
this.form.delRow(gridname,arrrow)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | 空 |
arrrow | 行集合 | 是 | Null |
单据表体删行时调用此函数,如果直接将表体数组置为空,删行无效。
xxxxxxxxxx
this.form.copyRow(gridname,arrrow)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | |
arrrow | 行集合 | 是 |
1、表格上可以设置属性“允许拷贝行”来启用,启用后在增删行按钮旁边显示拷贝行按钮
2、可以手动调用此函数拷贝行
3、拷贝后的行自动追加到最后
xxxxxxxxxx
this.form.setSelection(gridname,arrrow)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | 空 |
arrrow | 行集合 | 是 | Null |
Grid必须要有选择列
示例
xxxxxxxxxx
let griddata = this.form.data["ht_sobillmc"];//获取Grid数据
this.form.setSelection("grid_ht_sobillmc",[griddata[0],griddata[2]]);//将名称为grid_ht_sobillmc的表格,默认选中1、3两行
xxxxxxxxxx
this.form.clearSelection(gridname)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | 空 |
Grid必须要有选择列
xxxxxxxxxx
this.form.doFilter(gridname,callback)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | 空 |
callback | 回调函数,参数:arr | 是 | Null |
示例
xxxxxxxxxx
this.form.doFilter("grid_a_plan6",function(arr){
return arr.filter(a=>a["ddate"] == "03-19") //返回符合条件的结果集
})
APP原生无效
xxxxxxxxxx
this.form.clearTableFilter(gridname)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | 空 |
xxxxxxxxxx
this.form.setReferFieldValues(bhead,arrfield,gridname,arrrow)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
bhead | 是否表头字段 true/false | 是 | 空 |
arrfield | 参照关联字段集合,bhead为true有效,不填写时,系统检查所有表头/表体参照类型字段 | 否 | null |
gridname | 表格控件名称,bhead为false有效 | 否 | null |
arrrow | 表体行集合,bhead为false有效 | 否 | null |
注:表体增行函数 this.form.addRow的回调函数中,给参照关联字段赋值时,无需调用此函数,系统会自动带出其他参照字段。
示例
xxxxxxxxxx
//表头字段用法
this.form.data.cvencode = "001";//给参照关联字段赋值
this.form.setReferFieldValues(true,["cvencode"]);//刷新带出参照其他关联字段
//表体字段用法
this.form.data.billc[0]["cinvcode"] = "001";//第一行赋值
this.form.data.billc[1]["cinvcode"] = "001";//第二行赋值
let arr = [this.form.data.billc[0],this.form.data.billc[1]];//形成一个行集合
this.form.setReferFieldValues(false,null,"grid_billc",arr);//刷新带出参照其他关联字段
xxxxxxxxxx
this.form.showDetailRowForMobil(gridname,row) //此函数只有在移动端有效
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | 空 |
row | 需要显示的行,如果数据集中不存在此行,会报错 | 是 |
示例
xxxxxxxxxx
let row = this.form.data["数据源编号"][0];//获取table数据源第一条数据
this.form.showDetailRowForMobil("grid_a_plan6",row);
xxxxxxxxxx
this.form.filterdata //查询条件数据集合
数据结构:{字段名1:”值”,字段名2:”值”,字段名3:”值”,字段名4:”值”,字段名5:”值”}
字段名系统命名规则:查询编号_数据集编号
示例:列表deplist中,查询条件编号为f01,数据集编号为001,则字段名为f01_001
xxxxxxxxxx
let cfiltervalue = this.form.filterdata.f01_001 //获取改查询字段值
this.form.filterdata.f01_001 = "001" //给查询字段赋值
APP原生无效
xxxxxxxxxx
this.form.parent //父窗体对象
this.form.parent.data //父窗体表头值集合
this.form.parent.filterdata //父窗体查询字段数据集合
适用于通过 this.form.showDialog 函数打开的窗口访问父窗口,如果存在多级父窗口,通过this.form.parent.parent...来访问,具体层数取决于打开窗口的个数
说明:如果调用父窗体方法,建议使用this.form.callParent函数来解耦,尽量不要直接使用this.form.parent.方法名
示例:获取父窗体值赋值到新窗体
xxxxxxxxxx
if (this.form.formpara.copy==true){
this.form.addVoucher("button_add")
this.form.data.ccuscode=this.form.parent.data.ccuscode;
this.form.data.ccusname=this.form.parent.data.ccusname;
for(var i=0;i<this.form.parent.data.crm_orderc.length;i++){
this.form.data.crm_orderc[i].cinvcode=this.form.parent.data.crm_orderc[i].cinvcode;
this.form.data.crm_orderc[i].cinvname=this.form.parent.data.crm_orderc[i].cinvname;
}
}
xxxxxxxxxx
this.msg(message,type)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
message | 消息内容 | 是 | 空 |
type | 提示类型,1:主要 2:成功 3:信息 4:警告 5:危险 | 否 | {} |
xxxxxxxxxx
this.qichacha({path:"",keyval:""},function(ret){
})
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
path | 企查查API地址,填写接口地址中https://openapi.qcc.com/后面部分,如 EnterpriseInfo/Verify 或者 ECIInfoVerify/GetInfo?keyword=xx公司 | 是 | 空 |
keyval | 搜索关键字,支持企业名称、统一社会信用代码、注册号,只针对查询关键字是 searchKey 的有效,其他的请设为空 | 是 | {} |
cb | 回调函数,参数ret为查询结果 |
注意事项
1、如果环境为微云版,需在 appsettions.json中增加如下节点信息
xxxxxxxxxx
"Qichacha": {
"appKey": "",
"appSecret": ""
}
2、如果为标准版,需在web.config 的appSettings节点下增加如下信息
xxxxxxxxxx
<!--企查查参数配置-->
<add key="qcckey" value="" />
<add key="qccsecret" value="" />
弹窗显示自定义html内容
xxxxxxxxxx
this.pop(options,html)
options属性
属性 | 说明 | 必填 | 默认值 |
---|---|---|---|
title | 标题 | 否 | 空 |
modal | 是否遮罩 | 否 | true |
width | 宽度(可为百分百) | 否 | 500px |
示例
xxxxxxxxxx
this.pop({width:"600px",title:"日程查看"},`
<div>我的日程</div>
<div>上午,拜访客户</div>
<div>下午,学习提升</div>
`)
当前单据是否是待审批单据标志
xxxxxxxxxx
this.form.isapproving ;//true或者false
xxxxxxxxxx
this.form.loginInfo //登录信息对象
this.form.loginInfo.loginCode //登录人编号
this.form.loginInfo.loginName //登陆人名称
this.form.loginInfo.depCode //部门编号
this.form.loginInfo.depName //部门名称
this.form.loginInfo.corpCode //组织编号
this.form.loginInfo.corpName //组织名称
this.form.loginInfo.loginDate //登录日期
this.form.loginInfo.curDate //当前日期
this.form.loginInfo.posCode //职位编号
this.form.loginInfo.posName //职位名称
xxxxxxxxxx
let mtype = this.mobiletype();//wx:企业微信 dd:钉钉 app:聚恒V+
xxxxxxxxxx
let bpass = this.form.checkValidate()
//示例
if(bpass){
//通过
}
消除验证提示信息
xxxxxxxxxx
this.form.clearValidate()
消除验证提示信息
xxxxxxxxxx
/*
动态修改必填项
let ctl = this.form.findControl("txt_cmanage");
ctl.options.required = true;//设为必填项
ctl.regex = "";//设置正则验证表达式
*/
this.form.setRules()
此方法为异步方法,如需同步调用,请使用await
xxxxxxxxxx
(async) this.form.addVoucher(btnid,bodyrows)
//示例
await this.form.addVoucher("button_add",15)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
btnid | 执行新增按钮的ID,如果不传,则按钮状态不会发生变更 | 否 | 空 |
bodyrows | 表体默认新增行数 |
此方法为异步方法,如需同步调用,请使用await
xxxxxxxxxx
this.form.copyVoucher(keyid,callback)
//示例
await this.form.copyVoucher(this.form.data.keyid,function(){alert("拷贝成功")});//拷贝当前单据
此方法拷贝来源单据数据(字段是否可以拷贝在数据模型定义),并自动新增并将数据带入当前单据。
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
keyid | 需要拷贝得单据id值 | 是 | |
callback | 拷贝成功后的回调函数 | 否 |
xxxxxxxxxx
this.form.saveVoucher(cb);//系统默认使用的保存函数,保存成功后会有保存成功提示
this.form.saveVoucher1(cb);//保存成功后不会有成功提示,可在回调函数中自定义成功提示
此方法为异步方法,如果保存后做其他业务处理,代码写在回调函数当中。
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
cb | 回调函数,如 this.form.saveVoucher(function(){ }) | 否 | 空 |
APP原生无效
xxxxxxxxxx
this.form.saveTempData(cb)
此方法为异步方法,如果暂存后做其他业务处理,代码写在回调函数当中。
每一次暂存,系统都会按照日期记录暂存记录。
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
cb | 回调函数,如 this.form.saveTempData(function(){ }) | 否 | 空 |
APP原生无效
xxxxxxxxxx
this.form.loadTempData()
此方法为异步方法,如果暂存后做其他业务处理,代码写在回调函数当中。
如果只有一条暂存记录,系统会将数据直接加载到单据。
如果不止一条暂存记录,系统会弹出暂存记录选择窗口,手动选择载入暂存数据。
注意:如果使用暂存数据,请注意单据相关逻辑校验时效性,最好在服务端进行逻辑校验。
xxxxxxxxxx
this.form.deleteVoucher(btnid, cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
btnid | 删除按钮名称, | 否 | |
cb | 回调函数 | 否 |
此函数不会触发按钮状态变更
xxxxxxxxxx
this.form.deleteVoucherData(voucherno,keyid, cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
voucherno | 单据类型 | 否 | |
keyid | 单据ID | ||
cb | 回调函数 | 否 |
xxxxxxxxxx
this.form.commitVoucher(iscommit,cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
iscommit | 是否提交,true:提交 false:取消提交 | 否 | true |
cb | 提交成功回调函数 | 否 |
代码中调用,不会引发按钮状态修改
xxxxxxxxxx
this.form.commitVoucher1(voucherno,formid,keyid,iscommit,urgent, cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
iscommit | 是否提交,true:提交 false:取消提交 | 否 | true |
voucherno | 单据类型 | ||
formid | 单据对应的窗体ID | ||
keyid | 单据ID | ||
urgent | 紧急程度 0 一般 1 加急 | ||
cb | 提交成功回调函数 | 否 |
xxxxxxxxxx
this.form.showWorkFlow(vo);//vo:单据类型编号
APP原生无效
xxxxxxxxxx
this.refreshCache(cachetype,sourcecode,cb,errcb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
cachetype | component 构件 /list 列表/refer 参照/voucher 单据 | 是 | |
sourcecode | 构件时为空,其他分别为列表编号、参照类型、单据类型 | 是 | |
cb | 成功回调函数 | ||
errcb | 失败回调函数 |
####
汉字转拼音首字母
xxxxxxxxxx
let str = this.pinyin("你好");//返回 nh
汉字转拼音全拼
xxxxxxxxxx
let str = this.pinyinAll("你好");//返回 ni hao
说明:通过此函数便于生成速记码
此功能常用于通过一个地址打开带列表显示的Form窗体,通过地址传递查询条件参数,打开的列表条件自动赋值
如:我的待办列表地址:show?formid=b365133f-99da-4588-a48b-8f6d90ce6e67&f1_vi_bs_dsdj=报销单,其中f1_vi_bs_dsdj即为列表的查询条件编号,编号规则为:列表条件编号_列表编号,注:列表编号即为列表定义中的列表编号,也可以通过 console.log(this.form.filterdata) 来查看当前form窗体所有的查询条件编号。
所谓全局存储,就是在客户端会长期保留存储数据,即使浏览器关闭后数据也会保留
xxxxxxxxxx
this.$store //全局存储,浏览器关闭后再打开,数据依然能够获取到
this.$store.set(name,value) //数据存储
this.$store.get(name) //获取存储数据,如果存储的为对象,则返回该对象
this.$store.me //获取存储对象,也可以通过this.$store1[name]的方式获取存储数据
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
name | 存储键 | 是 | |
value | 存储内容,可以为字符串或对象 | 是 |
所谓临时存储,指的是只有当前浏览器窗口或标签页未关闭时才会保留数据
xxxxxxxxxx
this.$store1 //全局存储,浏览器关闭后再打开,数据依然能够获取到
this.$store1.set(name,value) //数据存储
this.$store1.get(name) //获取存储数据,如果存储的为对象,则返回该对象
this.$store1.me //获取存储对象,也可以通过this.$store1[name]的方式获取存储数据
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
name | 存储键 | 是 | |
value | 存储内容,可以为字符串或对象 | 是 |
xxxxxxxxxx
this.form.setHeadVisible(arrfield,visible)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
arrfield | 字段名数组 [“ccode”,”cname”] | 是 | |
visible | 是否显示 true/false | 是 |
xxxxxxxxxx
this.form.setBodyVisible(gridname,arrfield,visible)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | |
arrfield | 字段名数组 [“ccode”,”cname”] | 是 | |
visible | 是否显示 true/false | 是 |
xxxxxxxxxx
this.form.setBodyReadonly(gridname,arrfield,readonly)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格名称 | 是 | |
arrfield | 字段名数组 [“ccode”,”cname”] | 是 | |
readonly | 是否只读 true/false | 是 |
xxxxxxxxxx
this.form.setHeadReadonly(arrfield,readonly);
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
arrfield | 字段名数组 [“ccode”,”cname”] | 是 | |
readonly | 是否只读 true/false | 是 |
####
xxxxxxxxxx
this.form.columnSum(gridname/array,field) //返回值:计算结果
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname/array | 表格名称或者数组对象 | 是 | |
field | 字段名 | 是 |
示例
xxxxxxxxxx
let itotal = this.form.columnSum("grid_list","iquan")
//或者
let arr = this.form.data["so_billc"];//获取数组对象
let itotal = this.form.columnSum(arr,"iquan")
xxxxxxxxxx
this.doSum(arr,[field]);
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
arr | 数组,可以是值数组,如[1,2,3],也可以是对象数组,如 [{code:"001",quan:1},{code:"002",quan:20}] | 是 | |
field | 合计字段,如果是对象数组,通过该字段指定合计字段 | 否 |
传入一个一维数组,返回合计值,如果数组中存在空字符串或者null值,会自动按照0来处理。
通过此函数免去了繁琐的值类型判断
示例
xxxxxxxxxx
//值数组模式
let arr = ["aa",null,123,"222"]
let isum = this.doSum(arr);//返回值 345
//对象数组模式
let arr = [{code:"001",quan:1},{code:"002",quan:20}]
let isum = this.doSum(arr,"quan");//返回值 21
xxxxxxxxxx
this.form.inputControls;//控件数组
this.form.inputControls.forEach(item=>{
//循环控件数组,获取或修改控件状态
})
属性 | 说明 | 必填 | 默认值 |
---|---|---|---|
name | 名称 | ||
title | 字段描述 | ||
options.breadonly | 是否只读 true/false | ||
options.width | 宽度 数值或百分比 | ||
options.visible | 是否显示 true/false | ||
options.labelcolor | 字段描述颜色,如 red,green,#cccccc等 |
xxxxxxxxxx
this.form.gridControls;//控件数组
this.form.gridControls.forEach(item=>{
//循环控件数组,获取或修改控件状态
})
表格属性 | |||
---|---|---|---|
属性 | 说明 | 必填 | 默认值 |
name | 表格名称 | ||
cols | 列集合 | ||
size | 显示大小 medium / small / mini | ||
height | 高度,如 500px | ||
visible | 是否显示 true/false |
表格列属性 | |||
---|---|---|---|
属性 | 说明 | 必填 | 默认值 |
label | 列名 | ||
sortable | 允许排序 true/false | ||
resizable | 允许修改列宽 true/false | ||
align | 内容对齐方式 left/right/center | ||
headeralign | 列标题对齐方式 left/right/center | ||
visible | 是否显示 true/false | ||
editable | 是否允许编辑 |
示例
xxxxxxxxxx
let _this = this;
let grid = _this.form.gridControls.find(item=>item.name=="mygrid");
if(grid !=null){
_this.$set(grid,"visible",false) ;//隐藏表格
}
_this.form.gridControls.forEach(item=>{
//循环控件数组,获取或修改控件状态
_this.$set(item,"visible",false) //隐藏列
})
注意:必须使用 _this.$set 函数来操作属性,第一个参数为要修改的对象,第二个为需要修改的属性名,第三个为属性值。
xxxxxxxxxx
let arr = this.form.getVoucherAttachment();//获取附件信息,返回数组 示例:[{attclass:"01",filelist:[filename:"",fileid:"",maker:"",...]}]
xxxxxxxxxx
//keyid:数据集id
this.form.loadVoucherData(keyid,cb);//cb:回调函数
使用场景:点击树形节点查看单据信息
xxxxxxxxxx
this.form.loadVoucherData(row.keyid,function(){
})
xxxxxxxxxx
this.doSp(arr, approvetype, approvememo, cb);
参数 | 说明 |
---|---|
arr | 数组,如[{cvoucherno:"单据类型",cvoucherid:"单据ID",cnodeid:"审批节点ID",cversion:"审批版本号"}] |
approvetype | 审批状态 0 驳回,1 同意 |
approvememo | 审批意见 |
cb | 回调函数 |
示例:
xxxxxxxxxx
let arr = [{cvoucherno:"sobill",cvoucherid:"1",cnodeid:"rect1",cversion:"0000B875-76B6-4234-914B-11E03C8A1897"},{cvoucherno:"sobill",cvoucherid:"2",cnodeid:"rect2",cversion:"00012C96-CA94-4F97-BE3D-7271ED2C9721"}]
this.doSp(arr,"1","同意",function(res){
console.log(res)
})
xxxxxxxxxx
//gridname:列表名称
this.form.doExportList(gridname)
xxxxxxxxxx
//响应定义中主要借用定义的查询语句,获取查询结果
this.exportUserDefExcel("响应编号",params,"导出文件名")
params结构说明:
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
template | 模板名称(可选择按照模板导出) | 否 | |
para1 | 自定义参数1 | 否 | |
para2 | 自定义参数2 | 否 | |
... | 灵活定义 | 否 |
模板使用说明
模板文件是数据导出时,自动套用的格式文件,为标准的excel格式,模板文件编写完后放到 根目录/export_modal文件夹下
模板写法:
查询结果数据统一使用 {{table.字段名}}
params中的参数信息可以使用 {{参数名}}
示例
xxxxxxxxxx
this.exportUserDefExcel("0014",{template:"sale_modal",title:"销售统计表"},"导出文件.xlsx");//sale_modal对应根目录/export_modal中的sale_modal.xlsx
xxxxxxxxxx
//classcode:如果多文件上传:附件分类编号,来源于附件属性定义;如果单文件上传:附件使用控件名称
this.form.reloadAttachment(classcode)
如果窗体中没有数据模型,则该函数无效。
xxxxxxxxxx
this.form.showPrintModal(gridname,voucherno,keys,modalname,bpdf,cb)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
gridname | 表格控件名称,单据打印时置为空 | 是 | |
voucherno | 单据类型,列表打印时置为空 | 是 | |
keys | 单据ID集合,逗号隔开;单据打印时有效 | 否 | |
modalname | 模板文件名称,不填写默认打开模板选择对话框 | 否 | |
bpdf | 是否免预览,直接显示pdf结果,必须指定 modalname | 否 | false |
cb | bpdf为true时有效,打印地址生成后回调函数,参数:url(打印地址),可以回调函数中自定义新窗口打开pdf页面,通过return false 来阻止后续的默认操作 | 否 |
用法说明:
keys为选填参数,如果当前表单页面打印,不传则默认当前表单ID
xxxxxxxxxx
this.form.addTab({ ccode: "功能编号", cname: "功能名称", cpage: "页面地址",newwin:false })
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
ccode | 菜单编号(重复编号会自动切换至已经打开窗口) | 是 | |
cname | 菜单名称 | 是 | |
cpage | 菜单地址(相对或绝对地址) | 是 | |
newwin | 是否打开新浏览器窗口 | 否 | false |
xxxxxxxxxx
this.form.closeTab("功能编号")
xxxxxxxxxx
this.form.setButtonDisabled(arrbutton,isdisabled)
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
arrbutton | 按钮名称数组 | 是 | |
isdisabled | 是否不可用 | 是 |
xxxxxxxxxx
let myloadding = this.loading("正在加载");//显示遮罩
myloadding.close();//关闭遮罩
xxxxxxxxxx
let con = this.form.findControl(tabpanel)
this.$set(con.cols[0],"visible",false) //将tabpanel第一个页签隐藏
xxxxxxxxxx
let con = this.form.findControl(tabpanel)
this.$set(con,"visible",false)
注意:如果调用隐藏/显示函数后,还有其他逻辑代码处理,最好写在this.$nextTick 里面,代表状态变化完成后执行逻辑,示例如下:
xxxxxxxxxx
let con = this.form.findControl(tabpanel)
this.$set(con,"visible",true) //设置组件显示
this.$nextTick(()=>{
//在这里写后续的逻辑代码
})
xxxxxxxxxx
this.form.ismobile
xxxxxxxxxx
//browse:浏览状态;add:新增状态;edit:修改状态
this.form.vostate
xxxxxxxxxx
this.getScan(function(data){})
xxxxxxxxxx
this.getLocation(function(data){})
xxxxxxxxxx
data结构:
{name:"省市街道",address:"详细地址",lat:"经度",lng:"纬度"}
路由跳转只能用于不同form窗体之间的显示跳转,如果由form窗体跳转至其他非form页面,请用location.href来跳转。
xxxxxxxxxx
this.$router.push({path:路由地址,hash:""}) //如果强制路由跳转,hash参数赋值this.uuid()
//例如
this.$router.push("show?formid=123")
所谓子路由就是当前form窗体中拖入一个“路由”组件,通过子路由跳转来实现主页面部分区域的内容切换。
xxxxxxxxxx
this.form.loadPath(path, isalive);//path 路由地址 isalive 是否缓存页面,如果设置为缓存,从其他页面切回到当当前路由页面时,页面内容保持在上次切换前状态。
//例如
this.form.loadPath("show/show?formid=123",true)
格式化为 2021-02-01 格式的短日期
xxxxxxxxxx
this.getDate(date);//date:需要格式化的原始日期
//显示模板中写法为 {{getDate(值)}}
格式化为 2021-02-01 12:00:30 格式的长日期
xxxxxxxxxx
this.getDateTime(date);//date:需要格式化的原始日期
//显示模板中写法为 {{getDate(值)}}
如果日期年度为当前年度,则省略年度显示,只显示年-月,如果带时间,则显示为 年-月 时:分
xxxxxxxxxx
this.getDateTime1(date);//当年则显示 07-31,带时间显示为 07-31 12:20
根据日期获取日期描述,如日期为昨天,则显示昨天,最多到前天
xxxxxxxxxx
this.getDateDes(date);//昨天
xxxxxxxxxx
/*
num:数值
type:类型,取值范围:y(年) Q(季度) M(月) w(周) d(日) h(时) m(分) s(秒) ms (毫秒)
date:日期字符串,如 2022-03-01
*/
this.dateAdd(num,type,date) //返回值为长日期型,可以通过this.getDate(date)转为短日期
示例
xxxxxxxxxx
let mydate = this.dateAdd(10,'d','2022-03-01')
将数字取小数精度并安宅哦千分位格式化
xxxxxxxxxx
this.formatMoney(num,digital,zeroshow);//num:数字,digital:小数位数, zeroshow:true/false 是否显示小数点后面的0值,默认false
将数字按照精度进行四舍五入
xxxxxxxxxx
this.formatNumber(num,digital);//num:数字,digital:小数位数
类似于数据库 isnull函数,如果对象为空,则返回一个默认值
xxxxxxxxxx
this.isnull(obj,value);//如果obj为空,则返回value
xxxxxxxxxx
this.$md5(value);
####
采用对称算法对数据进行加密,服务端通过 Focus.Common.AESCryptoHelper.Decrypt进行解密
xxxxxxxxxx
this.encode(value);
对this.encode加密结果进行解密
xxxxxxxxxx
this.decode(value);
对JS对象序列化成Json字符串
xxxxxxxxxx
this.encodeObj(obj);
对JS对象复制为另一个对象
xxxxxxxxxx
this.copyObject(obj);
1、企业微信中,会自动调用微信的文件打开功能
2、App中,会自动选择本地的默认打开程序打开
xxxxxxxxxx
(await) this.showFile(url,filename);//此方法为异步方法
判断当前页面是否是在移动端打开
xxxxxxxxxx
this.bMobile();//返回值 true,false
客户端获取一个GUID
xxxxxxxxxx
this.uuid();//返回一个GUID字符串
根据名称获取控件对象,一般用于设定隐藏显示,可以通过隐藏父容器来达到批量隐藏的目的。
xxxxxxxxxx
this.form.getForm();//返回对象Json
//示例
let ctl = this.form.getForm();
this.$set(ctl,"bredirect",true); //开启转发
根据名称获取控件对象,一般用于设定隐藏显示,可以通过隐藏父容器来达到批量隐藏的目的。
xxxxxxxxxx
this.form.findControl(ctlname);//返回控件对象Json
//示例
let ctl = this.form.findControl("btton_add");
this.$set(ctl,"visible",false); //隐藏控件
根据名称获取控件对象,一般用于动态添加或删除下级节点(懒加载情况,如果非懒加载直接操作数据集即可)。
xxxxxxxxxx
let tree = this.form.getTreeComponent(ctlname);//返回控件组件对象
let mytree = tree.targetTree;//获取组件中的tree控件
动态添加下级节点(懒加载情况,如果非懒加载直接操作数据集即可)。
xxxxxxxxxx
let tree = this.form.getTreeComponent(ctlname);//返回控件组件对象
tree.append(data,node);//追加节点
参数 | 说明 | |
---|---|---|
data | 节点数据,对应树型控件数据源格式,例如{code:"",name:""} | |
node | 上级节点对象,在节点点击事件中获取 |
示例:
xxxxxxxxxx
let tree = this.form.getTreeComponent(ctlname);//返回控件组件对象
let parentnode = this.form.userpara.treenode;//节点点击事件中赋值,此处获取
tree.append({cname:"vvv",id:"123"},parentnode);
动态删除节点。
xxxxxxxxxx
let tree = this.form.getTreeComponent(ctlname);//返回控件组件对象
tree.remove(data/node);//删除节点
参数 | 说明 | |
---|---|---|
data | 节点数据或者节点对象都可以,在节点点击事件获取 |
示例:
xxxxxxxxxx
let tree = this.form.getTreeComponent(ctlname);//返回控件组件对象
let parentnode = this.form.userpara.treenode;//节点点击事件中赋值,此处获取
tree.remove(parentnode);
如果是空值,返回指定的内容,类似于数据库中的isnull函数。
xxxxxxxxxx
this.isnull(obj,value);//空值(含空字符串)转换函数
//示例
let obj = null;
let ret = this.isnull(obj,"123");//返回123
obj = "aaa";
let ret = this.isnull(obj,"123");//返回aaa
判断平台环境为微云版还是标准版。
xxxxxxxxxx
this.ServerType();//返回值: framework 和 core
拖入PDF查看控件,设置PDF地址(不再推荐,不兼容APP端)。
xxxxxxxxxx
let pdfctl = this.form.findControl("pdfview_1663038672072")
pdfctl.url = "http://bpm.sdfocus.com.cn/demo/123.pdf"
推荐用法一:PDF查看控件 -- 查看前事件,指定pdf地址
xxxxxxxxxx
config.url = "http://bpm.sdfocus.com.cn/demo/123.pdf";//PDF展现前指定地址
推荐用法二:通过this.form.showPdf(url)函数,一般通过按钮点击事件触发
xxxxxxxxxx
this.form.showPdf("http://bpm.sdfocus.com.cn/demo/123.pdf")
xxxxxxxxxx
this.form.copyVoucher(key);//key:被复制单据ID
xxxxxxxxxx
this.reflect(assname, classname, method, para, cb);//assname程序集名称,classname 类全名 method 方法名称,para:参数,如"0101",cb:回调函数
示例
xxxxxxxxxx
this.reflect("test.dll","Test.MyClass","GetCode","0101",function(str){
console.log(str)
})
窗体中需要拖放“视频播放”控件
xxxxxxxxxx
this.form.doPlay("组件名称","播放url地址");//播放地址可以为平台相对路径,比如平台根路径为http://localhost/his/,则可以写 123.mp4,代表http://localhost/his/123.mp4
示例
xxxxxxxxxx
this.form.doPlay("video_1654498807359","123.mp4")
窗体中需要拖放“IFrame”控件
xxxxxxxxxx
this.form.setIframeSrc("组件名称","url地址");//相对路径或完整地址
示例
xxxxxxxxxx
this.form.setIframeSrc("iframe_1654567153734","http://localhost/his")
提别说明:某些网站不支持在iframe中打开,比如百度,会提示页面无法显示。
日历视图显示的当前日期区间,返回长度为2的数组
xxxxxxxxxx
this.form.getCalendarViewPeriod("日历控件名称");//返回值 ["2024/10/01", "2024/11/01"]
自定义组件允许使用原生的HTML语言编写界面呈现,遵循Vue编写规范,可以在数据绑定容器中或者表单中使用,从工具箱拖入“自定义模板”,设置好相应数据源和脚本即可使用
自定义组件分为如下几部分:
1、显示模板,即html语言,双向数据绑定请使用v-model=“字段名”进行设置,如:
xxxxxxxxxx
<el-input v-model="code" placeholder="请输入内容" @change="test"></el-input>
2、组件挂载前,HTML呈现前的数据组织和处理,数据源准备,data中的数据都可以操作
3、组件挂载后,HTML数据呈现后,此时修改数据源,会关联界面更新,data中的数据都可以操作
4、组件函数定义,HTML中的事件及函数集中定义,比如值变更、获得焦点事件、点击事件等
5、监听函数定义,通过监听函数可以对data中的数据进行监听,数据一发生变化就会触发监听事件。
效果:
自定义组件中可用的属性:
1、this.form 获取根窗体对象
2、this.formdata 如果是在绑定容器中,为当前行数据,否则就是表单数据,通过console.log(this.formdata)看结构
3、this.form.remotedata 脚本、ajax、自定义查询、函数数据源
4、this.form.data 表单数据、列表数据源
了解Element组件,登录 https://element.eleme.cn/#/zh-CN/component/installation
在打印设计器中定义打印时,设计界面右侧的数据源默认只有单据或者列表的数据源,如下图
如果需要使用到其他相关数据查询结果(比如根据单据的某个字段关联查询其他数据),请遵从如下步骤
1、平台补丁更新8.9以上最新补丁包
2、右键编辑数据源
3、编写查询语句,如下图,查询语句中包含了一个参数 @opercode
4、参数设置界面新增一个参数,参数名为sql语句中的参数名称,取值表达式设定为从单据表中的相关字段
5、在打印设计界面拖入自定义数据源中的字段名称就可以
注意点:一定是在名称为 Connection的数据源上添加,该数据在打印时会自动读取平台对应的数据库,自定义添加的其他数据库连接只能存储在打印模板上,如果数据库连接环境发生变化就会打印出错。
所谓窗体中内容浮动显示是指当前窗体部分内容默认不显示,通过点击按钮侧边栏打开显示内容的方式,比如列表查询条件,默认比较占空间,可以通过浮动窗口的形式显示条件,点击查询后自动关闭浮动窗口,定义如下:
1、拖入显示块,浮动内容放置到显示块内部,同时指定弹出方式,窗口宽度和标题等。
2、指定触发的关联按钮,这样在点击按钮时,会自动打开下方内容,如果按钮点击时禁止自动打开关联浮动窗口,点击事件 return false 即可,也可以通过函数this.showPanel("显示块名称",true/false); 来代码控制。
效果
移动端优化点主要涉及到按钮显示和表格模板显示,其中按钮应该固定在页面最底部
如下图:
1、列表
2、单据
将按钮组放到显示块中,同时将显示位置设置为底部停靠
自定义模板内容,点击“示例”按钮,自动生成一个html模板,可以自行填写显示内容
列表查询条件分为两部分:1、单个搜索框,2、组合查询条件,如下图,点击漏斗图标打开组合条件窗口
1、添加列表组合查询条件,添加显示块容器,查询条件放到容器内
2、拖入一个快速过滤组件,指定关联筛选条件容器名称(即上一步拖入的显示块名称),在条件表达式中填写 return this.bMobile(); //只有移动端才显示
内部的查询按钮也设置容器关联,这样在点击查询的时候,会自动隐藏组合条件窗口
点击漏斗打开的组合条件效果
focus/go.htm文件记事本打开,修改 window.globalinfo对象,此处修改对全局有效,同时form窗体属性中也有同名的属性设定,如果在form窗体属性中单独指定了相应的值,则以form窗体设定为准
参数说明如下
xxxxxxxxxx
{
//字段标题属性
label:{
//字段标题颜色
color:{
normal:"#1d1f1f",//可编辑字段标题色
notnull:"#1d1f1f",//必填项字段标题色
readonly:"#757575" //只读字段标题色
},
font:{
size:"" //字段标题字体大小
}
},
//form窗体属性
formstyle:{
bgcolor:"",//窗体背景色
forecolor:"",//窗体前景色
margin:"10px",//窗体外边距
padding:"5px",//窗体内边距
headstyle:"" //表头字段显示风格,可选值:空白或者underline,如果为underline,则窗体前景色默认为白色,文本框字段只显示下边线
}
}
如需自定义修改其他样式内容,通过审查元素找到样式定义,然后在 focus/css/userdef.css 文件中,添加一个同名的样式,覆盖原有的样式内容
登录组件在窗体设计器中作为一个单独的控件,可以设定背景图片、登录验证方式、logo等相关参数,在登录成功后事件中实现跳转
此功能一般结合“开发定义-门户页面设定”功能一起使用,登录成功后跳转至设定好的门户页面
示例
xxxxxxxxxx
//登录成功后事件
let _this = this;
_this.$store.set("vendid", "01");//可选,可通过客户端缓存记录相关全局数据,窗体中可以通过 this.$store.get("vendid")获取到缓存数据
_this.$router.push({path:"main?key=a2eb50cf-6244-4411-951a-570f33701101"});//跳转至门户页面,key对应门户页面的ID,详见后面门户页面设定说明
//如果禁止登录,请不进行上一步路由跳转的同时,调用下面函数
_this.cancelLogin();//清除掉登录成功后的缓存记录
用来定义登录之后的导航显示框架界面
入口:控制面板 -- 开发定义 -- 门户页面设定
打开配置导航框架窗口
注意点:
1、在“首页地址”属性中,可以指定进入导航框架后自动打开的页面,填写窗体页面的相对路径或者数据中心页面的相对路径都可以
2、在“登录路由地址”中需要维护好登录页面的相对地址,用户在点击“退出”按钮后,保证能够跳转回原来的登录地址
点击预览时,此页面地址中的key值,即为登录窗口需要跳转的路由地址参数
业务流程图可以通过流程图的形式直观展现上下游业务单据的关联情况,例如根据到货单联查对应的上游采购订单
示例:
可以通过任何一个中间单据作为入口关联出上下游业务单据信息
“控制面板” -- ”报表及图形定义“ -- ”业务流程视图“
点击流程设计,进入图形界面
通过右键添加业务节点
点击节点,弹出的属性窗口
属性 | 说明 |
---|---|
数据模型 | 从数据模型列表选择对应数据模型 |
单据窗体ID | 该数据模型对应的单据窗体ID,用作联查时自动打开的窗体 |
字段集合 | 数据模型中的字段集合,包括表中不存在于数据模型的字段,数据模型增减字段后,点击左侧刷新按钮更新字段集合 |
自定义联查 | 系统默认打开“单据窗体ID"对应的窗体,可以在自定义联查中自定义打开方式 |
显示模板 | 运行时,节点上浮动显示的数据列表的显示模板,只能使用数据模型表头字段进行组合定义,如 单据编号:{code} |
权限控制 | 两种方式并集:1、填写菜单编号,系统按照菜单权限控制该节点数据是否允许联查明细;2、权限控制SQL,以pb_operator表的查询条件,如 copercode='admin' (只有admin才能查看) |
连线上点击右键 -- 属性设置
两个节点之间连线,设定关联条件,左右两侧的数据模型所有字段都可以作为条件关联
如果条件使用固定值,请使用单引号,如下图
点击“测试“按钮,输入参数,进行测试
定义好的流程,可以在业务单据中通过按钮快速打开业务流程视图
函数名
xxxxxxxxxx
this.form.showFlowMap(flowcode,key,voucherno,formid);//打开流程视图
参数说明:
参数 | 说明 | 允许空 |
---|---|---|
flowcode | 流程编号 | 否 |
key | 单据ID | 是,为空时默认取当前单据的ID(当前窗体需为单据窗体有效) |
voucherno | 数据模型编号 | 是,为空时默认取当前单据的数据模型变化(当前窗体需为单据窗体有效) |
formid | 窗体ID | 是,为空时默认取当前窗体的formid(当前窗体需为单据窗体有效) |
系统会按照 voucherno和formid从流程图中寻找入口节点,如果找不到入口节点会报错
第一步:按照两个数据模型的关联条件查询数据,由于数据模型下所有的表字段都可以作为关联条件,所以系统会查询出每一个表(包括主表和子表)的数据
第二步:数据模型下所有表数据查询完成后,遍历每一个子表,将主表中缺失ID自动根据子表进行补查填充
第三步:如果关联条件中有主表字段等于固定值的,会最终根据主表结果集进行筛选,将不符合固定值的记录删掉,并作为前台浮动展示的单据列表
倒计时组件需要是指根据一个指定的截止时间,自动计算并显示倒计时
倒计时开始时,会自动从服务器获取当前时间,然后根据这个时间进行倒计时
倒计时结束后自动执行该事件,参数:config 当前组件JSON数据
启动倒计时
xxxxxxxxxx
let t= this.form.getTimer("timer_1697159405917");//根据控件name获取控件对象
if(t != null){
t.startTimer("2023/10/12 15:30:12");//开启倒计时
}
电子地图使用的是高德地图,拓展开发可参考高德开发文档
1、到高德开发者中心注册应用,如下图
2、到go.htm中配置key和安全密钥
使用方法:窗体控件列表中,拖入地图控件即可
1、点选坐标,一般用于地图上选择一个位置
2、地图展现数据,通过在地图上一系列图标,旁边加以简短文字说明,通过点击事件穿透至详情。
1、标记点击事件,可进行联查打开平台窗体进行穿透查询
2、坐标拾取事件,通过这个事件获取坐标经纬度值
3、地图加载后事件,一般用于做自定义深入扩展,map参数为地图对象,参考高德开发文档进行深入扩展
4、标记显示后事件,一般在此处做设置地图中心点
1、获取地图组件对象
xxxxxxxxxx
let map = this.form.maps["组件名称"]
2、设置中心坐标
xxxxxxxxxx
map.setCenter(lng,lat);//经度、维度
示例:
1、定义一个数据集
2、配置地图
效果
图片菜单组件可以将一张图片虚拟分解成不同的区域,每个区域可以响应点击事件,比如一个功能流程图图片,图片中有一些按钮,可以将这些按钮通过坐标虚拟出进行定位并响应点击事件。
控制面板 - 开发定义 - 图片菜单定义
区域分解时,坐标值如何填写?
以分解“计划安排”按钮为例
首先用原始比例打开图片,可以直接点击表头的图片预览
第二,用qq或者微信截图工具,从图片左上角(左上角坐标值0 0),选择到“计划分解”按钮的左上角,记录坐标值,如下图所示
左上角坐标 :76 156
右下角坐标:299 210
最终坐标:76 156 299 210,将此值填写到坐标值列中即可,此种坐标格式适用于矩形形状,如果形状为圆形,则前两位代表坐标,最后一位代表半径大小,如 76 156 30 (圆心坐标 76 156,半径大小30)
第三,填写功能名称、功能编号、形状等信息,点击事件中会获取到这些信息。
“数据源编号”即为上一步定义的图片菜单定义编号,设置好长宽即可,如果长度设定,宽度按比例,则宽度可设置为auto
事件中定义点击事件:
表格自定义列支持两种模式:
1、模板显示,模板中可以通过html形式自由组织显示内容,参数row代表当前行数据
2、非模板显示,可以可视化添加按钮、标签、超链接等小组件。
此处着重介绍模板显示方式
模板中支持Element UI标准的html组件以及普通的html组件,模板中可以使用row参数进行动态绑定,row代表当前行数据,例如:{cinvcode:"001",cinvname:"钢材"},html使用时写法为: {{row.cinvname}}
示例
xxxxxxxxxx
<el-button type="primary" @click="$alert(row.cinvcode)" size="mini">{{row.cinvname}}</el-button>
说明:
模板中的可以通过this.form来使用Form窗体中的数据或方法,注意Html中不用填写this
示例
xxxxxxxxxx
<el-button type="primary" @click="$alert(form.data.ccode)" size="mini">{{row.cinvname}}</el-button>
概述
组织图可以通过关系图的形式展现节点之间的关系,比如树型层级图或者平面交错的关系图,典型的应用场景是用于可视化展现组织层级结构或者公司间的股权结构,几种效果如下:
树型
关系图
组件数据源是带有树型层级结构的Json对象,可通过平台Json结构定义来生成数据源,组件属性中选择数据源,填写主键字段、描述字段、子项字段即可完成基本的数据源设定,其他几个选填字段可以自由填写,如果需要动态设定不同节点的颜色、长宽等信息,需要根据实际情况填写其他字段。
示例
数据源结构(一个投资公司对外投资的股权占比数据)
xxxxxxxxxx
[
{
inode:"33",
cname:"投资公司",
irate:"",
items:[
{inode:"3301",cname:'公司A',crate:"占比30%"},
{inode:"3303",cname:'公司C',crate:"占比50%"},
{inode:"3304",cname:'公司D',crate:"占比50%"},
{inode:"3302",cname:'公司B',crate:"占比60%"}
]
}
]
字段填写
属性名称 | 填写值 | |
---|---|---|
主键 | inode | |
描述字段 | cname | |
子项字段 | items | |
连线描述字段 | crate |
展现效果:
组件支持两种自定义节点方式:1、HTML方式 2、节点模板方式
HTML方式可以通过组件事件"自定义节点格式",来动态输出节点的HTML显示内容,具体显示的内容格式,事件中nodedata参数代表当前节点的数据,可以根据实际需求,输出任何自定义的HTML内容及样式
这种方式的优点在于方便灵活,可以根据不同客户需求定义不同的节点显示样式;缺点同样明显,如果HTML中增加自定义事件,无法使用this.form来调用窗体对象。
示例:
xxxxxxxxxx
let str = `<div style="width:100%;height:60px;background:red">${nodedata.cname}</div>`
return str
节点模板方式是指通过Vue组件模板的方式来显示节点内容,模板中可以使用vue的标准指令以及ElementUI元素和form对象,如果模板中按钮需要触发form中的自定义方法,在Form窗体初始化事件中定义this.form.myFun = function(){...} ,模板按钮中直接通过 @click="form.myFun"即可触发事件;
节点模板的方式功能比较全面,模板中内容的长宽受限于控件默认节点长宽设定的限制
示例:
xxxxxxxxxx
<div style="height:100%;width:100%">
{{nodedata.cname}}
<el-button size="mini" @click="form.removeNodeById('orgrelation_1668568315594',nodeid)" type="success">设置</el-button>
</div>
在原有基础上追加显示节点
xxxxxxxxxx
this.form.addRelationNode(name,nodedata);//name:组件名称 nodedata:需要追加的json数据
nodedata详细说明
nodedata是一个json结构对象而不是数组,其格式跟数据源格式保持一致,例如上面示例中,在公司A下追加显示两个节点 A1和A2,写法如下
xxxxxxxxxx
{
inode:"3301",cname:'公司A',
items:[
{inode:"330101",cname:"公司A1",crate:"占比20%"},
{inode:"330101",cname:"公司A2",crate:"占比30%"}
]
}
/*
顶级节点必须是图形中已经有的节点,items表示需要在其下面需要追加的节点
*/
完整示例
xxxxxxxxxx
this.form.addRelationNode("orgrelation_1668568315594",{
inode:"3301",cname:'公司A',
items:[
{inode:"330101",cname:"公司A1",crate:"占比20%"},
{inode:"330101",cname:"公司A2",crate:"占比30%"}
]
});
从当前节点动态删除其中一个节点
xxxxxxxxxx
this.form.removeNodeById(name,nodeid); //name:组件名称,nodeid 节点id,可以通过节点点击事件获取到,实际上也是数据源设定中的主键字段值
上传两个文档,通过AI校验形成比对差异结果,支持BMP/JPEG/JPG/PNG/TIF/TIFF格式图片及Word/WPS/PDF/OFD格式文档,文件大小不超过10M
本组件采用百度AI进行比对,需提前在百度智能云中创建账号 https://login.bce.baidu.com/
在地址(https://ai.baidu.com/solution/contract)购买服务,对应的应用的 API Key 和 API Secret填写到平台中
标准版:web.config文件,增加下面配置节
xxxxxxxxxx
<!-- 百度智能文档接口〉-->
<add key="aidocappid" value="api key" />
<add key="aidocsecret" value="api secret" />
微云版:appsettings.json,添加下面配置节
xxxxxxxxxx
"BaiduAI": {
"AppKey": "api key",
"AppSecret": "api secret"
}
可以在“组件加载前”事件中,动态指定文件目录
如:docinfo.path = "doc/123.docx" (注意斜杠的类型,不要用反斜杠)
地址为基于平台程序目录的相对路径
微云版:基于wwwroot文件夹的相对路径
标准版:基于程序根目录的相对路径
界面效果
概述
该组件为纯H5模式的文档编辑器,支持PC端和移动端,支持多人同时编辑以及修订留痕。
该组件集成的WPS提供的编辑器,服务器需要能够访问外网,并且能够对外映射服务端口
1、配置Focus.WPS服务
打开appsettings.json文件,配置服务端口号、数据库连接以及HostUrl,其中HostUrl代表服务对应的外网访问地址
示例
xxxxxxxxxx
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
/*启动参数*/
"IsHttps": false,
"CertName": "server.pfx",
"CertPwd": "A9qxcg34",
"ServerIP": "www.xxx.top",
"ServerPort": 8866,
/*end 启动参数*/
"AllowedHosts": "*",
"DbOption": {
"ConnectionString": "数据库连接字符串(base64编码)",
"DbModel": "SqlServer"
},
"HostUrl": "http://jh.focusinsoft.com:8866" //该服务对应的外网地址
}
配置完成后,双击 Focus.Wps.exe启动服务,如下图
1、工具栏拖入"WPS文档"组件,配置AppID(https://solution.wps.cn/?source=docs 上面申请)
2、WPS控制台中新建应用,并配置回调网关(回调地址即为系统部署中配置的"HostUrl"地址)
3、填写需要预览或者编辑的 文件目录和文件名称,设定好文档权限,如下图
注意:文件目录指的是基于平台根目录的相对路径
微云版:如mydoc指的就是 wwwroot目录下的mydoc文件夹,如果配置文件中指定了“FilePath”,则是该指定目录下的文件夹
标准版:如mydoc指的就是 平台虚拟目录根目录下的mydoc文件夹
4、如需动态修改文档权限或者文件名称,通过“文档控件加载前事件”修改参数,如下图
通过docpara修改文档权限,通过docinfo动态修改文档的文件夹及文档名称。
xxxxxxxxxx
//docpara:
{
"comment": this.config.comment?1:0,//是否具有评论文档权限
"copy": this.config.copy ? 1 : 0, // 是否具有拷贝文档内容权限
"download": this.config.download ? 1 : 0, //是否具有下载文档权限
"print": this.config.print ? 1 : 0, // 是否具有打印文档权限
"read": this.config.read ? 1 : 0, //是否具有预览权限
"update": this.config.update ? 1 : 0//是否具有编辑权限
}
//docinfo
{
"focus_usercode":usercode,// 当前登录用户编号
"focus_path": this.config.docpath,// 文档路径
"focus_filename": this.config.filename,// 文档名称
}
说明:所有设置好的文档信息统一存储在表 bs_docinfo 中。
5、如需自定义扩展其他功能,通过“文档控件加载完事件”获取到的instance(文档对象)进行操作,API文档请参考 https://solution.wps.cn/docs/start/summary.html
6、web应用配置
微云版:appsettings.json添加配置节
xxxxxxxxxx
"WebDoc": {
"AppID": "WPS APPID"
},
标准版:web.config添加
xxxxxxxxxx
<add key="wpsappid" value="WPS APPID" />
1、获取文档组件对象
xxxxxxxxxx
let doccomp = this.form.docinstance["wpsdoc_1729074007973"] //文档名称
let docinstance = doccomp.docinstance;//文档组件中的WPS文档对象,通过这个对象扩展文档(参考https://solution.wps.cn/docs/client/api/Word/Document.html)
2、手动初始化文档
xxxxxxxxxx
let doccomp = this.form.docinstance["wpsdoc_1729074007973"] //文档名称
await doccomp.initDocument()
3、根据文档模板生成文档
xxxxxxxxxx
let doccomp = this.form.docinstance["wpsdoc_1729074007973"] //文档名称
/*
第一个参数:模板ID,即模板表(bs_doctemplate的id值)
第二个参数:单据ID,即对应数据模型主表的id值
*/
await doccomp.createDocByTemplate("aff9955b-2ab4-45f7-80cc-815fb3286104","02ab2fbd-b3dd-4b43-878b-1f58e446e9f8")
4、接收/拒绝所有文档修订
xxxxxxxxxx
let doccomp = this.form.docinstance["wpsdoc_1729074007973"] //文档名称
doccomp.AcceptAll();//接受
doccomp.RejectAll();//拒绝
5、显示水印
xxxxxxxxxx
let doccomp = this.form.docinstance["wpsdoc_1729074007973"] //文档名称
/*
info结构:{FillStyle:"rgba(192, 192, 192, 0.6)",Font:"bold 20px Serif",Value:"水印内容"}
FillStyle 字体颜色
Font 字体类型及大小
Value 水印内容
*/
doccomp.SetWatermark(info);
文档模板是指根据单据定义对应的word文档模板,可以根据单据内容动态填充模板生成新的word文件。
控制面板 -- 开发定义 -- word模板定义
模板中需指定对应的数据模型,手动指定模板编号和名称,保存后,点击“设计模板”打开设计窗口,如下图
表头字段定义方式:在右侧点击表头自动,系统会自动添加占位符至文档内容中,如上图“合同日期”
表体字段定义方式:
1、在word中先画一个表格,填写表格的列表,表格可能有多个,需要记住word中的表格序号
2、点击表体中的某一个表体描述,打开窗口如下:
在右侧窗口中选择“明细信息”表体对应的表格序号,同时按照“列顺序号”指定每列对应的绑定字段名称。
3、模板定义完成后,点击“保存配置”完成模板保存。
定义的模板文件存储位置:
微云版: 根目录/wwwrrot/doctemplate
标准版:根目录/doctemplate
根据定义的文档模板套用单据数据生成文档。
1、新建窗体,拖入WPS文档组件,“禁止自动加载”打钩,填写“文件目录”(不填写则默认docbuild文件夹)
2、窗体格式加载后调用文档生成函数
xxxxxxxxxx
let instance = this.form.docinstance["wpsdoc_1730704874180"] //根据文档名称获取文档控件对象
/*
第一个参数:模板ID,即模板表(bs_doctemplate的id值)
第二个参数:单据ID,即对应数据模型主表的id值
*/
await instance.createDocByTemplate("aff9955b-2ab4-45f7-80cc-815fb3286104","02ab2fbd-b3dd-4b43-878b-1f58e446e9f8")
运行结果如下:
生成的文档可以只允许预览或者允许多人同时编辑。
概述
需要安装浏览器Active插件(NTKO插件),不支持移动端
文档在线编辑组件允许在浏览器中打开或编辑word、excel、ppt文档,支持文档的服务器端保存,使用时将“在线文档”组件拖入到Form窗体,设置属性即可。
文档使用的两种模式:“二进制字段”、“文件”,在属性“保存类型”中设定即可
1、二进制字段是指文档内容最终作为一个二进制字段内容保存在单据中的表头字段,数据模型中设置表头字段类型为“二进制”即可,在“绑定字段名”属性填写二进制字段名
2、文件是指,保存类型选择“文件”,“文件目录”填写相对于根目录的虚拟目录名称,如 mydoc/files ,"文件名“字段填写需要加载的实际文件,如 方案.docx,此时控件会按照路径加载文档,保存时会自动保存至该目录下。
打开 应用程序根目录/focus/office/set.json ,分别输入ProductCaption(公司名称)和ProductKey(授权序列号)保存
每个客户端需要安装购买后厂家给的exe安装程序。
xxxxxxxxxx
this.form.loadDocument(name,path,file);//name:控件名称;path:目录名称;file:文件名
xxxxxxxxxx
this.form.saveDocument(name);//name:控件名称;
xxxxxxxxxx
this.form.getDocElement(name).myDoc ;//name:控件名称;通过文档对象,可以获取最终文档控件对象,可以参考ntko开发手册做其他自定义控制
文档控件初始化完成后执行的事件,如果需要通过函数打开文档,需要在此处填写代码
事件参数:name 文档控件名称
常见问题:
1、如果控件安装后还是无法加载,需要拷贝focus/office/ npffax.dll至 C:\Windows\SysWOW64 2、如果打开文档时,在外部跳出work或者Excel,客户端解压并执行 focus/office/文档打开方式注册.zip
1、pc端在线编辑word文档时,添加批注或者审阅状态时,如果修改人默认不是当前登录用户,需要设置一下本地office,如下:
a) 打开word
b)打开选项设置
c) 最终效果
自定义组件允许你通过原生element(或原生html)+vue的模式来编写组件展示模板和逻辑,形成一个公用的自定义组件,以便于在Form窗体或者数据中心和数据大屏中方便的拖拽使用。
element参考地址:https://element.eleme.cn/#/zh-CN
自定义组件分为七大部分:
1、组件Data:自定义组件的Data数据,所有组件内元素都可以绑定Data内变量,这是组件的数据源中心。
1、HTML:直接编写html显示界面,可以通过element ui 或者原生html形式去编写。
2、创建后(created): 这个时候,组件中的data、method已被初始化,属性也被绑定,但是此时还是虚拟dom,真实dom还没生成,$el 还不可用。这个时候可以调用data和method的数据及方法,created钩子函数是最早可以调用data和method的,故一般在此对数据进行初始化。
3、Mounted( 挂载后):此时模板已经被渲染成真实DOM,用户已经可以看到渲染完成的页面,页面的数据也是通过双向绑定显示data中的数据。 这实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了。
4、自定义方法(Methods): 自定义组件中所有方法的定义集合,包括Html元素中事件对应的方法,都在此处进行定义。
5、监听(Watch):监听是指对某一个变量进行自动感知,如果变量值发生变化,会立即触发监听方法,Data或者this.form.data 内的变量都可以监听。
6、样式(css):用于定义组件的css样式集合,Html元素中可以直接通过class来使用样式。为了避免自定义样式对其他页面元素造成污染,需要给组件定义一个[组件css名称](组件右侧属性中定义),这个css名称会作用于组件顶级元素上,自定义的css样式都基于[组件css名称]去限定作用范围,如 组件css名称定义为 mycomp, 子项css定义方式如下:
xxxxxxxxxx
.mycomp .head{
color:red;
}
.mycomp .body{
font-size:20px;
}
1、访问父窗体
自定义组件通过this.form来获取父窗体对象,可以通过this.form来访问到父窗体所有的数据和方法
2、在父窗体中访问组件
通过 this.form.getMyComp("组件名称") 来获取组件对象,然后可以通过该对象访问组件内部的数据和方法
xxxxxxxxxx
let mycomp = this.form.getMyComp("compname");
let str = mycomp.code;//获取组件Data中的code变量值
mycomp.setCols("grid_dep");//调用组件中的setCols方法
3、组件内部变量formdata
formdata是form窗体默认传递到自定义组件的一个数据集,作为系统默认的数据集对象,具体内容分为以下两种情形:
如果组件位于绑定容器内,比如 数据绑定设置为是的卡片控件内部,由于卡片是根据数据源循环输出的,卡片内部的控件数据集就变成当前循环内部当前行数据
如果组件位于非绑定容器内,则为from窗体中的data数据,等价于this.form.data
平台ORC识别支持薪火科技和华为两种类型的OCR发票识别
web.config文件,appSettings节点下增加如下节点
xxxxxxxxxx
<add key="ocrtype" value="xinhuo" /><!--huawei或者xinhuo-->
<add key="ocrinvoice" value="3a644949614beed11fde1298bb1d0026" /><!--薪火OCR的token值-->
<add key="ocrurl_xinhuo" value="http://api.xinhuokj.com:40072/ocr" /><!--固定不用动(薪火API地址)-->
<add key="ocrurl" value="http://localhost:30881/invoice/OcrInvoice" /><!--如果是华为,填写平台ocr微服务地址-->
appsettings.json, RestAPI节点下增加如下节点:
xxxxxxxxxx
"Ocr": {
"OcrType": "xinhuo", //huawei或者xinhuo
"OcrUrl_Xinhuo": "http://api.xinhuokj.com:40072/ocr", //薪火科技ocr请求地址,固定不动
"OcrAPI": "http://api.xinhuokj.com:40072/ocr", //如果是华为,需填写API地址
"Token": "3a644949614beed11fde1298bb1d0026" //如果是薪火科技,填写对应token
}
OCR识别包括发票、卡证(银行卡、身份证、社保卡、护照、驾驶证、行驶证、户口本、名片、房产证、不动产证、营业执照、开户许可证、税务登记证、组织机构代码证、车辆合格证、车辆登记证、香港身份证、结婚证、离婚证)、普通OCR三种,其中华为OCR只支持发票识别,其他的需使用薪火OCR识别服务(https://www.xinhuokj.com/)
目前只有华为的OCR识别需要单独部署OCR识别服务
1、部署OCR识别服务,OCR识别服务为独立的.net core Web API服务,通过修改appsettings.json来设定端口号及SSL证书(可选)
2、修改appsettings.json中的OCR配置见,设置 “AK","SK","Region"信息
此信息在 华为管理后台-我的凭证 中可以查看
2、双击Focus.OcrAPI.exe启动OCR服务
Form窗体中,设置“附件”-"智能识别"属性,如下图:
识别结果位于"bs_invoice"表,字段 cdata 存储识别之后的原始Json数据,可根据需要使用。
发票识别后,按照cinvoice_haoma(发票号码)和cinvoice_daima(发票代码)两个字段检查是否有重复,如果有重复,则认为发票已经被识别过,再次识别不会被存入我的票夹。
普通邮箱(SMTP协议):填写 系统邮箱地址、邮箱用户名(跟系统邮箱地址相同)、邮箱密码、邮箱类型选择SMTP
exchange邮箱:
系统邮箱地址(实际发件箱地址)
邮箱用户名:如果为域用户,填写 域名\邮箱账号(@符号前面部分),否则直接填写邮箱地址或者@符号前面部分
邮箱密码、邮箱类型选择exchange
exchange设置示例
非域邮箱
域邮箱
注意:
点击邮箱测试,系统会向系统邮箱中发送一封测试邮件,如果收取成功,则代表邮箱配置完成。
1、标准版,web.config 文件,appSettings 下增加 autytype和domainip两个配置节,如下;
xxxxxxxxxx
<add key="autytype" value="ad" />
<add key="domainip" value="域服务器IP" />
2、微云版,appsettings.json,增加如下配置节
xxxxxxxxxx
"license": {
"authtype": "ad",
"domainip": "域服务器IP"
}
从8.7版开始,定时任务统一使用core版的 Focus.Scheduler
Host配置点说明:
参数名 | 说明 |
---|---|
Env | 标准版:framework 微云版:core |
Url | 标准版:web服务地址,http://平台地址/smsservice/batchsend.asmx;微云版:http://平台地址 |
UserCode | 微云版有效 |
Pwd | 微云版有效 |
模型计算主要通过定义计算模型来批量处理复杂计算,通过字段级公式定义计算逻辑,循环计算对象集合来批量计算并存储计算结果
应用场景:
1、薪资计算,每个人都有不同的薪资项目,不同薪资项目有不同的计算逻辑,尤其是绩效相关的薪资项目,需要从不同业务表中汇总计算绩效奖金
2、成本计算,按照产品汇总计算每一个成本项目成本
3、其他涉及到多类别数据汇总统计的场景
1、计算对象,通过sql语句获取需要计算的对象集合
2、模型参数,模型计算需要用到的参数,比如 年度、月度等等,在计算模型的时候需要传入指定的参数
3、存储表,计算结果存储的表名及相关字段,模型计算完毕后自动按照规则存储到指定的数据表
4、计算公式,针对存储表的每一个字段,定义计算公式,通过js语言可以方便定义公式
字段说明:
主表
字段 | 说明 |
---|---|
查询方式 | sql或存储过程,针对”查询语句“字段 |
数据库连接 | ”查询语句“所用的数据库连接 |
存储表名 | 计算结果的存储表名称,必须是平台数据库的一个实表 |
主键字段 | 存储实表的主键,如果为GUID,平台会自动生成 |
查询语句 | 需要计算对象的sql查询语句,基于改查询结果进行循环计算,可用 {参数名}直接使用入口参数值 |
线程分组字段 | 基于查询结果集中的字段进行分组,执行模型计算时,系统按照分组字段建立独立的执行线程,进行并行计算 |
计算前SQL | 模型计算前执行的SQL语句,一般用于计算前清空数据,可用 {参数名}直接使用入口参数值 |
计算后SQL | 计算完成后执行的SQL语句,一般用作计算完毕的后处理,可用 {参数名}直接使用入口参数值 |
明细信息子表
字段 | 说明 |
---|---|
计算顺序号 | 字段计算的先后顺序 |
字段名 | 表头”存储表名“中的字段名 |
字段描述 | 字段说明 |
计算公式 | 该字段值的计算公式定义,详见公式说明 |
入口参数子表
字段 | 说明 |
---|---|
顺序号 | 参数的顺序编号 |
入口参数名称 | 实际的参数名,如depcode、invcode |
入口参数说明 | 参数说明 |
公式分为三部分:
1、字段及函数,字段是指公式中可使用的字段,具体是指存储表中的字段;函数主要包括平台函数和其他函数,平台函数是在平台中定义的函数(C#语言定义),其他函数都属于通用的JS函数
2、公式编写窗口,在此处编写计算逻辑,语法为标准js,可以在js中直接调用C#定义的平台函数
3、全局参数,模型定义中的入口参数
代码编写说明:
1、系统默认提供的参数,主要有三个:item 、rowdata、params、log
item:当前循环的计算对象,即”查询语句“对应查询结果中的一行,根据截图中的SQL为示例
xxxxxxxxxx
var percode = item.copercode;//获取当前循环计算对象的用户编号
var depcode = item.cdepcode;//获取当前循环计算对象的部门编号
rowdata:当前计算结果的存放对象,即”存储表“对应的当前行,因为字段有计算顺序,因此后面计算的字段公式中可以通过rowdata获取到前面计算的结果
xxxxxxxxxx
var isales = rowdata.isales;//获取到isales字段计算的结果
params:获取参数值
xxxxxxxxxx
var iyear = params.year;//获取年度参数值
var imonth = params.month;//获取月度参数值
2、平台函数,函数是在报表模块-自定义函数中进行定义,平台函数全部为C#定义的函数,使用方法:
xxxxxxxxxx
var isales = focus.Call("getSales",item.copercode);//调用getSales的自定义函数并获取执行结果,凡是focus开头的都是C#预定义的函数
除了自定义函数外,还可以在js中执行sql查询语句,返回结果为格式为数组的字符串,需要通过JSON.parse转为对象
xxxxxxxxxx
var dt = focus.QueryData("select cdepcode,cdepname from dep ");//获取查询结果字符串
var arr = JSON.parse(dt);//将结果字符串转成数组对象,如 [{opercode:"demo1",name:"demo1"},{opercode:"demo2",name:"demo2"}]
for(var i=0;i<arr.length;i++){
var opercode = arr[i].opercode;//获取当前行字段值
}
3、params :获取模型参数
xxxxxxxxxx
var year = params.year;//获取年度参数
4、log:日志记录组件,用于调试问题,日志文件存储在logfile 文件夹下
xxxxxxxxxx
log.Info("日志");//记录info日志
log.Debug("日志");//记录debug日志
log.Error("日志");//记录错误日志
如何强制开启日志
浏览器打开如下地址:
1、微云版: http://平台地址/plat/pluginlog
2、标准版:http://平台地址/sysplat/dataget/data.ashx?type=pluginlog
1、前端调用
xxxxxxxxxx
let _this = this;
//isdebug:调试模式,istest:测试模型(仅计算,不保存数据)
this.doCompute("模型编号",{参数名:值,参数名:值,isdeug:"1",istest:"1"},function(ret){
//处理执行结果
if(ret.bsuc){
}
else{
_this.msg(ret.msg,4);
}
})
2、后台调用
添加Focus.Formula.dll
xxxxxxxxxx
var fmanage = new ComputManager(cmd);
fmanage.IsTest = true;//开启测试模式
fmanage.IsDebug = true;//开启调试模式
var result = fmanage.DoCompute("模型编号", "01","03");//模型编号后面为模型参数,多个参数逗号分开即可。
测试模式:系统只执行函数计算,结果不实际保存到数据表,有异常会提示,可用于检查有无公式错误
调试模式:系统记录详细的日志,日志文件位于 logfile \ *_debug.log
只有微云版才可以指定附件存储位置,可以指定应用服务器任意一个文件夹作为附件存储文件夹
appsettings.json文件,配置FilePath节,如下图
xxxxxxxxxx
"FilePath": "F:\\focusCore\\FocusApp\\FocusApp\\FileUpload"
老版单据:单据格式 - 修改定义,选择归档时机和归档路径
新版单据:在数据模型同名的老版单据格式 - 修改定义,选择归档时机和归档路径
归档路径支持固定路径和动态路径两种方式(需提前在系统参数中设置好“文档中心路径”)
固定路径:路径名字为静态,如 /项目管理/预算编制
动态路径:根据单据表头字段值动态组成路径,如 /项目管理/{cperiod} ,其中 cperiod为表头字段名,根据实际字段值形成实际路径。
自定义消息推送是指将自定义消息推送至微信、钉钉、app或者语音电话等不同移动端,消息类型分为文本消息和通知消息两种,通知消息带链接。
设置步骤:
1、向bs_wxmessage表预置数据,表结构说明:
字段名 | 字段说明 | 备注 |
---|---|---|
copercode | 人员编号 | 必填 |
ctitle | 消息标题(微信/钉钉/app起作用) | 必填 |
cmsg | 消息内容(200字以内) | 必填 |
ctype | 消息类型(text/img可选) | 必填 text:文本消息; img:通知消息,可带链接 |
curl | 链接url地址 | 必填 ctype填写img时有效 |
ddate | 插入日期 | 必填 |
bvoice | 是否电话语音消息 | 必填 1或0 |
cappid | 应用ID | 非必填,企业微信/钉钉应用ID,如果填写,消息会推送到对应应用里面,为空时默认推送到审批应用中 |
bsend | 是否已发送 | 留空 系统自动回写 |
cerror | 微信/钉钉发送错误日志 | 留空 |
capplog | App消息发送结果日志 | 留空 |
bdisplay | 是否已展示 | 留空 系统自动回写 |
cvoiceid | 电话语音消息ID | 留空 系统自动回写,可根据此字段查询bs_voicesend(语音消息表(接听结果表) |
cvoicelog | 电话语音发送结果日志 | 留空 系统自动回写 |
2、调用推送服务,推送消息
标准版推送地址:http://平台地址/smsservice/batchsend.asmx,方法名:BatchSendWxMessage
微云版推送地址:http://平台地址/focusapi/BatchSendWxMsg
可将服务配置到定时任务,实现消息定时自动发送。
电话语音是指通过拨打电话的形式对提醒或者预警消息进行推送,系统自动记录发送日志和接听状态。
bs_wxmessage中配置的bvoice为1的消息,都会以电话语音的形式推送
账户申请地址:https://www.ihuyi.com/
标准版:web.config中增加配置节,如下:
xxxxxxxxxx
<!--语音消息配置,如需开通服务,需从平台申请语音电话账号和秘钥-->
<add key="voiceapiid" value="" />
<add key="voiceapikey" value="" />
<add key="voiceapiurl" value="http://bpm.sdfocus.com.cn:8089" />
<!--end 语音消息配置-->
微云版:appsettings.json中RestAPI节点下增加配置节如下:
xxxxxxxxxx
"RestAPI": {
//如需开通服务,需从平台申请语音电话账号和秘钥
"Voice": {
"ApiId": "",//
"ApiKey": "",
"VoiceAPI": "http://bpm.sdfocus.com.cn:8089" //平台发送服务地址
}
}
标准版:
http://平台地址/smsservice/batchsend.asmx,方法名:SendVoice
此请求为Web服务
微云版:
推送地址:http://平台地址/focusapi/Voice通过Post方式,参数分别为:mobile 【手机号】 content【通知内容】
此api需要鉴权,鉴权方式参考平台对外接口说明
语音消息发送日志在表 bs_voicesend
窗体事件 - 串口消息事件,通过data参数传入读数
类库扩展是指对桌面应用程序进行功能扩展,从而增强本地交互能力,扩展的类库可以在网页中进行调用并获取到返回值
三步走:
自定义一个类库,如FocusPortal.UserPlugin,定义一个类,格式如下:
xxxxxxxxxx
[ClassInterface(ClassInterfaceType.AutoDual)] //必须要有
[ComVisible(true)]//必须要有
public class Test
{
public string DoTest(string str)
{
return "aaa:" + str;
}
}
编译后得到FocusPortal.UserPlugin.dll 文件,上传到桌面应用程序根目录
桌面应用程序根目录下找到 plugin.json 文件,记事本打开,配置自定义类
xxxxxxxxxx
{
"plugins": [
{
"name": "MyObj",/*自定义名字,前台调用时用到*/
"classname": "FocusPortal.UserPlugin.Test", //全类名
"assembly": "FocusPortal.UserPlugin" //程序集名称
}
]
}
脚本写法
xxxxxxxxxx
let srvObj = (window.chrome.webview.hostObjects.MyObj) //固定写法,声明桌面应用程序接口对象,MyObj 为配置文件的name
let str = await srvObj.DoTest("dad");//DoTest为类中自定义的方法
本功能必须基于桌面应用程序实现。
高拍仪上传附件时,不能使用单据本身的附件上传按钮,需要自定义个新的按钮,在按钮事件中调用高拍仪接口。
按钮点击打开拍照窗口:
xxxxxxxxxx
let srvObj = (window.chrome.webview.hostObjects.JsCommon) //固定写法,声明桌面应用程序接口对象
//获取单据附件ID值,vofileid为附件ID,单据新增时为一个临时guid,单据修改时等于单据ID
if(this.form.data.vofileid == ""){
this.form.data.vofileid = this.uuid();
}
//打开拍照窗口,返回值:上传完成的文件名称集合
var str1 = await srvObj.CaptureImg(this.form.voucherinfo.voucherno,this.form.data.vofileid,"01");//CaptureImg参数说明:第一个参数 单据类型;第二个参数 单据附件id; 第三个参数 附件分类编号
返回值str1格式:
{
"imglist":["d2543aff-e58f-4556-abb2-64514d1bd107.jpg,6ffb2ef0-00a8-4e71-a225-185a4ab03843.jpg"],
"barcode":["6938351600072","6938351600072"]
}
服务号后台域名设定:
1、JS接口安全域名设定
2、网页帐号中的 [网页授权获取用户基本信息] 设定
3、平台系统参数中配置“微信服务号”相关参数
菜单挂接:
常规菜单地址后面增加fw=1参数,如:
http://bpm.sdfocus.com.cn/demo/focus/go.htm#/show?formid=d6025b28-5714-4743-84db-32d422b19886&fw=1
打开连接时,平台会自动检查pb_operator表中copenid是否存在,如果为空,则会自动跳转到登录窗口,登录完成后,copenid字段会自动跟用户编号绑定,下次打开时即可免登录打开功能。
要点说明:
微信服务号中挂接的单据如果希望能够获取到登录信息,需要将对应的Form窗体设为“登录验证”
设为登录验证的单据在服务号中打开时,会自动进行登录校验,如果未发现登录信息,会自动跳转到微信端的登录窗口,输入用户名和密码之后,完成登录的绑定,下次再打开单据时,会自动完成登录。
如果希望能够跳转到自定义的页面(比如自定义的用户注册界面)而不是平台自带的微信登录窗口,可以在窗体的“登录失败”事件中获取到事件中的"openid"参数,然后携带该参数跳转到自定义页面,最终此openid能够写入到 pb_operator表 copenid字段后,也相当于完成了用户的绑定,下次再打开该单据系统会自动完成登录。
服务号消息应该使用模板消息来发送,否则会有推送数量限制,模板消息参考 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#类目模板消息
1、微信中申请模板消息模板ID
2、微信中配置模板显示格式
3、在平台 bs_wxsrvmessage 表中插入需要发送的消息
字段名 | 描述 | 说明 |
---|---|---|
id | 唯一主键 | 自定义生成 |
cpercode | 接收人员编号 | 人员表中copenid有对应值才可以,服务号中打开平台功能时会自动获取到 |
ctemplateid | 申请的模板ID | |
cdata | 模板消息数据 | 对应微信json数据的data部分,如下图 |
curl | 消息对应url链接 | 可为空,赋值后点击消息可打开链接页面 |
ddate | 消息生成日期 | 系统只发送距离现在时间不超过1小时的消息 |
bsend | 是否已发送 | 发送后平台会自动更新为1,插入消息时无需给此字段赋值 |
cret | 发送返回值 | 发送后平台自动更新,发送结果返回值 |
4、调用平台的批量发送微信消息接口(BatchSendWxMessage)就可以完成推送
新建一个构件,引用wxinterface.dll程序集,发送代码如下
xxxxxxxxxx
weixin wx = new weixin();
Seejee.DataCommon.DACommand cmd = new Seejee.DataCommon.DACommand();
string str1 = wx.SendTemplateMsg(cmd, "admin", "ZAKepZydjEBjg5lHwA10ZYAMaY-631e-vMBjbnH3g5o", "\"aa\":{\"value\":\"123\",\"color\":\"red\"},\"bb\":{\"value\":\"test\",\"color\":\"aaabbb\"}","");
Response.Write(str1);
cmd.Close();
SendTemplateMsg(cmd,usercode,strid,data,url) 参数说明:
usercode:接收人编号(人员表中copenid有对应值才可以)
Strid:模板编号
url:消息链接地址
Data:模板参数,如下部分
从8.8版本开始,待办事项在钉钉上会自动推送到钉钉待办中,配置点如下:
1、通讯录权限
2、待办应用权限
3、用户表PB_Operator中 cddcode必须要填写钉钉通讯录中的用户编号
钉钉端待办效果
根据父级部门ID获取子级部门列表
xxxxxxxxxx
//depid=父级部门ID
//微云版,第一个参数地址为 plat/wxdeplist?depid=1
//微信接口
this.getRemotData("sysplat/dataget/data.ashx?type=wxdeplist&depid=1","get",{},function(str){
console.log(str)
})
//钉钉接口
this.getRemotData("sysplat/dataget/data.ashx?type=dddeplist&depid=1","get",{},function(str){
console.log(str)
})
部门数据结构参考
钉钉:https://open.dingtalk.com/document/orgapp/update-a-department-v2
微信:https://developer.work.weixin.qq.com/document/path/90206
xxxxxxxxxx
//badd:新增为1,修改为0
//微云版,第一个参数地址为 plat/wxdepset?badd=1
let depinfo = {};//部门数据结构
//微信
this.getRemotData("sysplat/dataget/data.ashx?type=wxdepset&badd=0","post",{depinfo:JSON.stringify(depinfo)},function(str){
console.log(str)
})
//钉钉
this.getRemotData("sysplat/dataget/data.ashx?type=dddepset&badd=0","post",{depinfo:JSON.stringify(depinfo)},function(str){
console.log(str)
})
xxxxxxxxxx
//depid:需要删除的部门ID
//微云版,第一个参数地址为 plat/wxdepdel?depid=1
//微信
this.getRemotData("sysplat/dataget/data.ashx?type=wxdepdel&depid=1","get",{},function(str){
console.log(str)
})
//钉钉
this.getRemotData("sysplat/dataget/data.ashx?type=dddepdel&depid=1","get",{},function(str){
console.log(str)
})
xxxxxxxxxx
//depid:部门ID
//微云版,第一个参数地址为 plat/wxperlist?depid=1
//微信
this.getRemotData("sysplat/dataget/data.ashx?type=wxperlist&depid=1","get",{},function(str){
console.log(str)
})
//钉钉
this.getRemotData("sysplat/dataget/data.ashx?type=ddperlist&depid=1","get",{},function(str){
console.log(str)
})
xxxxxxxxxx
//userid:人员ID
//微云版,第一个参数地址为 plat/wxper?userid=1
//微信
this.getRemotData("sysplat/dataget/data.ashx?type=wxper&userid=1","get",{},function(str){
console.log(str)
})
//钉钉
this.getRemotData("sysplat/dataget/data.ashx?type=ddper&userid=1","get",{},function(str){
console.log(str)
})
人员数据接口参考
钉钉:https://open.dingtalk.com/document/orgapp/user-information-creation
微信:https://developer.work.weixin.qq.com/document/path/90195
xxxxxxxxxx
//badd:新增为1,修改为0
//微云版,第一个参数地址为 plat/wxperset?badd=1
let perinfo = {};//人员数据结构,一般先通过"获取人员详细信息"接口获取,再修改
//微信
this.getRemotData("sysplat/dataget/data.ashx?type=wxperset&badd=0","post",{perinfo:JSON.stringify(perinfo)},function(str){
console.log(str)
})
//钉钉
this.getRemotData("sysplat/dataget/data.ashx?type=ddperset&badd=0","post",{perinfo:JSON.stringify(perinfo)},function(str){
console.log(str)
})
xxxxxxxxxx
//userid:人员ID
//微云版,第一个参数地址为 plat/wxperdel?userid=1
//微信
this.getRemotData("sysplat/dataget/data.ashx?type=wxperdel&userid=1","get",{},function(str){
console.log(str)
})
//钉钉
this.getRemotData("sysplat/dataget/data.ashx?type=ddperdel&userid=1","get",{},function(str){
console.log(str)
})
表格控件数据源可以为列表、ajax、script、自定义查询 几种
如果表格控件数据源为列表,表格的分页会自动完成,只需设置下分页大小和分页方式即可
如果数据源为非列表,分页方式定义如下:
1、数据源返回结果中必须要有 focus_totalcount列,代表总记录数
2、实现表格控件的"分页点击事件",事件中会传入新的页号参数pageno,通过这个参数,手动刷新对应的数据源
示例:
定义一个自定义查询数据源,sql中加上了分页条件,同时有一个 focus_totalcount(此处为了方便演示,固定为2000)
编写分页事件
xxxxxxxxxx
let pagesize = config.pagesize;//获取分页大小
this.form.loadUserDefData("bbb",{num1:(pageno-1)*pagesize+1,num2:(pageno)*pagesize});//刷新自定义数据源,参数起始序号和结束序号
return false
容器数据绑定是指做外层容器绑定数据源,根据数据源的记录数重复输出容器内容并进行容器内的数据绑定
应用场景一般为动态重复输出复杂格式的内容,或者表体通过容器数据绑定来实现卡片式录入而不是表格式录入。
只有”卡片“控件支持数据源绑定,将”数据绑定“属性开启,并选择对应的数据源,如下图
卡片内部可以拖入其他控件如布局控件、文本框、图片、多选组、日期文本框等等,容器内部的控件绑定数据源方式为:在”绑定字段名“属性中,手动输入卡片容器数据源中的字段名
容器内部控件的值变更事件统一在”卡片“控件的”值变更事件“中处理
统一通过 this.currentitem 来获取当前行数据或者给字段赋值
如果想让app自动更新
需要如下步骤
1、将最新的app安装包命名为 app.apk,并上传至平台如下目录:
微云版 : wwwroot根目录下
标准版:程序文件夹根目录下
2、更新bs_appver表为最新的版本号
注:app版本号在 “我的”-“版本信息”中
标准版使用iis作为服务,有时会有不支持apk扩展名文件下载的情况,请在iis 的 MIME类型中,添加类型支持,如下图
配置位置:
URL地址:
1、标准版:http://平台地址/wx/corp.aspx
2、微云版:http://平台地址/wx/CorpAuth
Token:
自定义或随机获取,需同步修改平台系统参数对应的Token
EncodingAESKey
随机获取并填写至平台系统参数EncodingAESKey下
为了统一微信、钉钉、App三端的入口页面风格,微信和钉钉菜单导航统一使用新版地址,如下:
导航地址写法:
1、http://平台根地址/focus/go.htm#/app 显示所有菜单编号以m_menu开头的菜单项
2、http://平台根地址/focus/go.htm#/app?mcode=001显示菜单编号以001开头的菜单项
如果挂接钉钉菜单,注意地址参数应该有agentid
App上打开扫码,此函数是打开的手机摄像头进行扫码。
xxxxxxxxxx
this.postAppMessage({type:"扫码",call:function(){
let _this = this;
app.scanCode({
success: function (res) {
_this.callBack(res)
}
});
}})
扫码结果处理,Form窗体的 "form属性 - 事件 - App消息事件" 中填写扫码逻辑
xxxxxxxxxx
if(data.type == "扫码"){
let res = data.data;//扫码结果,可通过alert(JSON.stringify(res))查看对象结构
alert(JSON.stringify(res));
}
打开PDA页面,在桌面应用中打开扫描工具,在扫描工具中的应用配置中招待开发者项按钮点击后输入默认密码:888888后,可查看到具体的广播名称及键值。具体操作如下:
1、在桌面应用中找到扫描工具。
2、进入扫描工具后点击应用设置后,在应用设置页面点击开发者项。
3、点击开发者项,输入默认密码888888后进入页面开发者项页面,可查看广播名称及键值名称。
4、点击app登录页面中系统设定。
5、在此页面将PDA中的广播名称配置到对应的广播信息中,保存即可。
App上打开扫码,此函数是响应PDA专用扫码头扫码。
凡是支持广播的PDA或者RFID扫码都可以支持,推荐配置 6G+64G
第一步:配置广播动作和广播标签,在 "App登陆界面-系统设定" 中配置
第二部:定义消息处理函数,在 "form属性 - 事件 - App消息事件" 中编写业务逻辑
xxxxxxxxxx
//App消息事件
//PDA扫码和RFID感应,事件类型都是 appscan
if(data.type == "appscan"){
this.msg("扫码结果:"+data.data)
}
xxxxxxxxxx
let cphone= "15812345678";
var cphone = '1111';
this.postAppMessage({
type: "电话", data: { num: cphone }, call: function () {
app.makePhoneCall({
phoneNumber: data.num//仅为示例
});
}
})
xxxxxxxxxx
this.postAppMessage({type:"设备",call:function(){
let _this = this;
app.getSystemInfo({
success: function (res) {
_this.callBack(res)
}
});
}})
识别结果处理,参照App消息事件章节
只针对特定型号:
品牌:汉印
型号:HM-Z3
打印指令参考:https://www.docin.com/p-2160105026.html
xxxxxxxxxx
//打印指令,即相关格式内容
let data = "! 0 200 200 550 1\r\n" +
"PAGE - WIDTH 576\r\n" +
"BOX 0 0 576 450 2\r\n" +
"LINE 0 88 576 88 1\r\n" +
"LINE 0 216 576 216 1\r\n" +
"CENTER\r\n" +
"T 8 0 0 40 入库单 \r\n" +
"LEFT\r\n" +"LEFT\r\n" +
"T 8 0 5 100 小车号:123 制单人:aaa 日期:20220101\r\n\r\n" +
"T 8 0 5 130 品种:a 规格:b 形式:c \r\n" +
"T 8 0 5 160 单据编号:123 \r\n" +
/*表体*/
"T 8 0 10 240 姓名\r\n" +
"T 8 0 450 240 数量\r\n" +
"PRINT\r\n" ;
this.postAppMessage({
type:"print",
data:{printdata:data},
call:function(){
this.doPrintData(data.printdata)
}
})
需开启手机蓝牙(个别机型需同时开启定位服务)
第一次打印时,系统会弹出打印设置对话框,如下图
需要先点击“搜索打印机”,搜索成功后,点击“打印测试”,成功打印后返回即可。
xxxxxxxxxx
this.postAppMessage({type:"定位",call:function(){
let _this = this;
app.getLocation({
type: 'wgs84',//默认为 wgs84 返回 gps 坐标,gcj02 返回国测局坐标
geocode:true,//是否解析地址信息
success: function (res) {
_this.callBack(res)
}
});
}})
address(res.address)地址数据结构:
参数 | 说明 |
---|---|
country | 国家 |
province | 省份名称 |
city | 城市名称 |
district | 区(县)名称 |
street | 街道信息 |
streetNum | 获取街道门牌号信息 |
poiName | POI信息 |
postalCode | 邮政编码 |
cityCode | 城市代码 |
识别结果处理,参照App消息事件章节
xxxxxxxxxx
this.postAppMessage({type:"位置",call:function(){
let _this = this;
app.chooseLocation({
success: function (res) {
//回调消息推送
_this.callBack(res)
}
});
}})
选择结果处理,参照App消息事件章节
xxxxxxxxxx
this.postAppMessage({type:"closewindow",call:function(){
app.navigateBack();
})
xxxxxxxxxx
let myinfo = {
nickName: '昵称',
lastName: '张',
firstName: '三',
remark: '备注',
mobilePhoneNumber: '112545454',
weChatNumber: 'wx123',
workPhoneNumber:'工作电话',
workFaxNumber:"传真",
email:"电子邮件",
success: function (res) {
_this.callBack(res) //一定注意用 _this
},
fail: function () {
console.log('fail');
}
}
this.postAppMessage({type:"联系人",data:myinfo,call:function(){
let _this = this;
app.addPhoneContact({
nickName: data.nickName ,
lastName: data.lastName,
firstName: data.firstName,
remark: data.remark,
mobilePhoneNumber: data.mobilePhoneNumber,
weChatNumber: data.weChatNumber,
workPhoneNumber:data.workPhoneNumber,
workFaxNumber:data.workFaxNumber,
email:data.email,
success: function (res) {
_this.callBack(res) //一定注意用 _this
},
fail: function () {
console.log('fail');
}
});
}})
xxxxxxxxxx
let url = 'http://bpm.sdfocus.com.cn/demo/sysplat/dataget/files/testfiledownload.docx'
this.postAppMessage({type:"文档",data:url,call:function(){
let _this = this;
app.downloadFile({
url: data ,
success: function (res) {
let filePath = res.tempFilePath;
app.openDocument({
filePath: filePath,
showMenu: true,
success: function (res) {
console.log('打开文档成功');
}
});
}
});
}})
扫码或者获取位置后,需要进行相应的回调,从而获取函数执行结果,APP中的API是在App底座执行,执行完成后会以消息的形式发送到页面的窗体中的“App消息事件”当中去,在App事件中进行统一的消息处理。
微云版消息事件定义位置:
老版单据消息事件定义位置:
单据格式加载后写如下代码:
xxxxxxxxxx
window.vue ={
appCallBack=function(data){
//处理结果
}
}
点击扫码按钮扫码回调示例
平台单据为标准的H5页面,H5页面跟App通讯通过消息事件的方式进行,比如 H5页面发起一个消息调用,APP执行完成之后通过回调函数通知H5页面。
消息发起函数:
xxxxxxxxxx
this.postAppMessage({
type:"自定义类型",//消息类型
data:{code:'123',name:'张山'},//消息参数
call:function(){
app.makePhoneCall({ //消息动作,参考App相关API:拨打电话
phoneNumber: '114' //仅为示例
});
}
})
注意:如果消息动作的API中需要使用变量,需要定义 data 消息参数,然后API中通过 data.参数名 获取值。
示例:
xxxxxxxxxx
let phonenum = "13553229988"
this.postAppMessage({
type:"callphone",//消息类型
data{num:phonenum},//将phonenum放到num参数中
call:function(){
app.makePhoneCall({
phoneNumber: data.num //通过data.num获取,data为消息对象
});
}
})
按钮点击打开扫码事件:
xxxxxxxxxx
this.postAppMessage({type:"扫码",data:{},call:function(){
let _this = this;
app.scanCode({
success: function (res) {
//回调消息推送
_this.callBack(res) //一定注意用 _this
}
});
}})
Form窗体app消息事件
xxxxxxxxxx
if(data.type == "扫码"){
let res = data.data;//扫码结果,可通过alert(JSON.stringify(res))查看对象结构
}
按钮点击打开扫码事件:
xxxxxxxxxx
postAppMessage({type:"扫码",data:{},call:function(){
let _this = this;
app.scanCode({
success: function (res) {
//回调消息推送
_this.callBack(res)
}
});
})
单据格式加载后事件
xxxxxxxxxx
window.vue ={
appCallBack=function(data){
if(data.type == "扫码"){
let res = data.data;//扫码结果,可通过alert(JSON.stringify(res))查看对象结构
}
}
}
#####
一个组件为一组控件集合单元,为了便于复用,可以将常用的显示组合定义成一个组件,这样在定义dashboard页面的时候,可以直接建预定义的组件拖拽至页面当中使用。组件内部对象指的是组件内部不同控件之间进行联动的时候使用的对象,在关联刷新数据时,需要使用组件内部对象进行操作。
xxxxxxxxxx
this.part //当前组件
this.part.loadUserDefData(dscode, params) //刷新组件内部某一个自定义查询数据集
this.part.loadListData(gridname, para) //刷新组件内部列表查询数据集,用法同Form设计同名函数
this.part.loadAjaxData(dscode, para) //刷新组件内部Ajax数据集,用法同Form设计同名函数
this.part.loadScriptData(dscode, para) //刷新组件内部自定义函数数据集,用法同Form设计同名函数
在综合数据展现窗体中,刷新窗体中的自定义组件
xxxxxxxxxx
this.form.loadPartData(partname, params);//控件数组
参数 | 说明 | 必填 | 默认值 |
---|---|---|---|
partname | 组件名称 | 是 | |
params | 组件参数,针对组件数据源,1、如果数据源为数据列表,则参数为查询编号 2、如果数据源为自定义查询,参数为sql语句中的{参数名},如 select coperame from pb_operator where copercode=‘{opercode}’,则opercode为查询参数 3、如果为Ajax请求或者自定义函数,则参数为数据源中定义的参数名。 | ||
嵌套绑定是指根据一个主数据源,通过预定义的显示模板系统自动循环输出相同格式的内容块,如下图:
定义方式:
1、拖入一个“卡片”控件作为模板容器,在卡片控件上选择需要绑定的数据源
2、将相应的数据控件拖入卡片容器内部,将“使用父容器数据源”属性设为true,然后填写相应的绑定字段
卡片控件设置如下图:
模板内容
模板内控件事件中获取当前行数据的方法:
xxxxxxxxxx
this.formdata //当前行数据,例如按钮点击事件。
xxxxxxxxxx
{
type: "map",
defcolor: "#ccc",
bordercolor: "green",
titlecolor: "#ccc",
subtitlecolor: "#ccc",
showvisulmap: true,
height: 400,
datasource: "",
title: "地图",
subtitle:"子标题",
visualmap: {
min: 800,
max: 50000,
text: ['High', 'Low'],
textstyle: {
color: "#ccc"
},
realtime: false,
inrange: {
color: ['#27E5F2', '#F1F50B', '#F87205']
}
},
text: "地图",
width: "100%",
title: "地图"
}
xxxxxxxxxx
{
name: '山东',
value: 20057.34
},
{
name: '浙江',
value: 15477.48
}
配置项 | 说明 | |
---|---|---|
默认显示地区名称 | 地图打开时默认展示的地区,一般为中国或者各省、直辖市名称或者地级市县名称 | |
子地名字段 | 数据源中对应显示子地名的字段名,一般为默认地区所属的地市或县名称 | |
值字段 | 子地名对应的数值字段名 | |
默认图层颜色 | 无数据的地区图层显示颜色 | |
边线颜色 | 图层边线颜色 | |
显示虚拟化视图 | 地图左下角显示最大值到最小值颜色变化趋势的图 | |
最大值 | 数值最大值 | |
最小值 | 数值最小值 |
clickevent :地图点击事件
参数名 | 说明 |
---|---|
para | 当前元素数据,para.name:当前点击地名,点击事件中通过 this.config.areaname=“山东”来修改当前默认显示地图 |
xxxxxxxxxx
{
type: 'focusboxmenu',
name: '',
options: {
datasource:"",//数据源
fontsize: 12,//字体大小
itemwidth: 80,//元素宽度
itemheight: 80,//元素高度
span:6 //元素所占栅格数
}
}
xxxxxxxxxx
[
{icon:'icon iconfont jhui-RectangleCopy16',label:'测试1',color:'green'},
{icon:'icon iconfont jhui-RectangleCopy16',label:'测试2',color:'blue'}
]
配置项 | 说明 | |
---|---|---|
字体大小 | 每个元素下面显示文本的字体,为数字类型 | |
元素占用栅格数 | 每个元素占用几个栅格,横向一共24栅格,如果设置为6,则一行最多显示4个元素 | |
元素宽度 | 每个元素的宽度,数字类型 | |
元素高度 | 每个元素的高度,数字类型 | |
数据源 | 数据源格式必须为数组,如:[{icon:'icon iconfont jhui-RectangleCopy16',label:'测试1',color:'green'},{icon:'icon iconfont jhui-RectangleCopy16',label:'测试2',color:'blue'}] | |
显示文本字段名 | 文本显示部分需要绑定的字段名,如 label | |
显示图标字段名 | 对应的字体图标字段名,如 icon | |
显示颜色字段名 | 每个元素的背景色字段,如 color |
clickevent :元素点击事件
参数名 | 说明 |
---|---|
data | 当前元素数据,通过 data.属性名 或者 data[属性名] 来获取属性值 |
卡片菜单一般用于显示模块内部的功能导航或者一些业务上的局部导航,也可以用作列表数据显示
卡片菜单可以放在带数据源的卡片控件中循环输出,也可以作为普通控件独立使用
选择数据源 》填写标题字段、值字段 》 定义点击事件 》完成
效果:
卡片菜单控件在带数据源卡片控件内部,根据当前行数据动态显示不同数据
勾选开启菜单卡片的”容器绑定“属性 》选择数据源 》填写内容标题字段、值字段、卡片标题字段 》 定义点击事件 》完成
思路:卡片菜单数据源仍然使用一个总的数据源,在”行过滤事件“中根据卡片控件当前行数据在卡片菜单数据源中进行过滤,并返回过滤之后的结果数组,如下图:
运行结果:
xxxxxxxxxx
var log = Focus.Common.Logger.GetLogger<当前类名>();//core版本
var log = FocusLog.LogHelper.GetInstance(); //framework版
log.Error(error.Message, error);//记录错误日志
log.Info("日志");//记录普通信息
说明:
core版需要引用 Focus.Common.dll 、log4net.dll程序集
framework版需要引用Focus.Log.dll、log4net.dll程序集
微云版注意事项:为保证运行时环境相关dll资源的正确使用,建议通过平台的扩展插件工程 Focus.UserPlugins进行自定义构件扩展,下载链接:
http://jh.focusinsoft.com/Focus.UserPlugins.rar
简介:平台即时通讯为web端的通讯工具,简称“聚微讯”,可通过点击右上角通讯图标直接打开,界面风格类似微信界面,可点对点进行消息通讯。
独立的部署方式,服务器必须安装.net core3.1以上,将chat程序文件拷贝到服务器,点击Focus.Soket.exe打开服务窗口即可,在appsettings.json中配置数据库连接以及启动端口号,如果多个服务运行在同一个服务器,应该将Redis配置节下面的InstanceName修改成不一样的,防止缓存信息共用。
平台中引入即时通讯的方式
在平台“系统参数”设置中,填写即时通讯服务地址即“Soket地址”字段,比如“http://bpm.sdfocus.com.cn:8001/”
消息隔离区域是指平台为了能够处理不同场景的即时通讯需求,比如SRM中的即时通讯、平台用户自身的即时通讯、售后管理中的即时通讯等,每一个模块可能都有独立的用户体系,不同模块之间即时通讯都是独立的,因此加入了消息隔离区域的概念,不同的隔离区域编号对应各自一套用户体系,相互之间互不影响。
隔离区域通过后台表控制,表名:bs_imuser
表结构
字段名 | 描述 |
---|---|
id | 主键字段 |
cregion | 区域编号 |
cusercode | 用户编号 |
cusername | 用户名称 |
cdefine1 | 自定义项1 |
cdefine2 | 自定义项2 |
cdefine3 | 自定义项3 |
cdefine4 | 自定义项4 |
cdefine5 | 自定义项5 |
cdefine6 | 自定义项6 |
cdefine7 | 自定义项7 |
cdefine8 | 自定义项8 |
clockfilter | 通讯录条件 |
上图为隔离区域为001下面所有的用户
clockfilter用法:上图所示,001用户能够看到cdefine1为0的所有用户,除001之外的用户只能看到001用户,也就是说他们只能跟自己能看到的用户进行聊天,从而控制了不同人员之间的聊天权限。
即时通讯窗口也就是一个url地址,地址中需要标识隔离区域编号和当前用户
地址格式:
http(s)://消息服务地址/chat/go.htm#/chat?info=参数
info参数格式:[区域编号,当前用户编号] ,即用逗号隔开的两个值合成的字符串,然后进行encode编码
示例:
xxxxxxxxxx
let region="001",usercode="admin"
let info = this.encode(region+","+usercode);
let url = "http://bpm.sdfocus.com.cn:8001/chat/go.htm#/chat?info="+encodeURIComponent(info);
window.open(url);
平台支持通过调用web API的形式进行消息发送,具体可以登录消息服务地址
http(s)://消息服务地址/swagger/index.html
例如:http://bpm.sdfocus.com.cn:8001/swagger/index.html
API地址:http(s)://消息服务地址/chat/send
消息参数:
参数名:message
参数值:{region: "system",From: "admin",FromName: "超级管理员",FileName: "",FileSize: "",To: "demo3",message: "你好",id: "123123",type: "text"}
参数名 | 参数说明 | 其他说明 |
---|---|---|
region | 隔离区域编号 | |
From | 发送者编号 | |
FromName | 发送者名称 | |
FileName | 文件名称 | |
FileSize | 文件大小 | |
To | 接收者编号 | |
message | 消息内容 | |
id | 消息ID | 最好使用GUID |
type | 消息类型 | text / image /file 三种 |
插件是指在平台标准功能中可以插入式调用的自定义功能逻辑,构件属于插件的一部分,插件功能包括:ERP(u8、金蝶云星空、U9)接口封装、web服务调用、web API调用、自定义程序集调用、存储过程调用七大部分。
插件管理包括插件数据集定义、插件定义和单据插件配置三大部分。
插件的目的就是为了打通平台对其他系统的调用接口。
插件数据集是指插件中所使用的数据来源定义,可理解为查询一个带有主从关系或者单主表的数据,在执行插件前,系统会先进行数据集的获取,插件中可以根据数据集自定义组织参数信息。
入口: 控制面板 -- 开发定义 -- 插件数据集定义
1、添加数据源,在左上角点击“添加数据源”按钮,填写数据源编号、名称信息
2、添加数据集,点击右上角“添加数据集”按钮,弹窗中输入数据集定义,一个数据源中只允许有一个“数据主表”,并且只能有一个主键字段,支持“SQL语句”和“存储过程”两种查询方式,根据自己的实际情况,分别组织主表数据查询和其他子表的数据查询
入口:控制面板 -- 开发定义 -- 插件定义
1、金蝶云星空
账套ID、授权用户、授权应用ID、应用秘钥、服务URL地址为必填项,均为云星空提供的接口参数,FormID为对应接口单据标志,从官方API文档获得,动作包括增删改、提交、审核等等,代表需要执行的操作。
表体“参数信息”中,配置需要用到参数,如单据保存方法
xxxxxxxxxx
//金蝶官方文档实例
//注意 1:此处不再使用参数形式传入用户名及密码等敏感信息,改为在登录配置文件中设置。
//注意 2:必须先配置第三方系统登录授权信息后,再进行业务操作,详情参考各语言版本SDK介绍中的登录配置文件说明。
//读取配置,初始化SDK
K3CloudApi client = new K3CloudApi();
//用于记录结果
StringBuilder Info = new StringBuilder();
//业务对象标识
string formId = "ER_ExpenseRequest";
//请求参数,要求为json字符串
string jsonData = "{\"NeedUpDateFields\":[],\"NeedReturnFields\":[],\"IsDeleteEntry\":\"true\",\"SubSystemId\":\"\",\"IsVerifyBaseDataField\":\"false\",\"IsEntryBatchFill\":\"true\",\"ValidateFlag\":\"true\",\"NumberSearch\":\"true\",\"IsAutoAdjustField\":\"false\",\"InterationFlags\":\"\",\"IgnoreInterationFlag\":\"\",\"IsControlPrecision\":\"false\",\"ValidateRepeatJson\":\"false\",\"Model\":{\"FID\":0,\"FDate\":\"1900-01-01\",\"FStaffID\":{\"FSTAFFNUMBER\":\"\"},\"FDeptID\":{\"FNUMBER\":\"\"},\"FReason\":\"\",\"FOrgID\":{\"FNumber\":\"\"},\"FPhoneNumber\":\"\",\"FCostOrgID\":{\"FNumber\":\"\"},\"FCostDeptID\":{\"FNUMBER\":\"\"},\"FCurrencyID\":{\"FNUMBER\":\"\"},\"FSettleTypeID\":{\"FNUMBER\":\"\"},\"FPayOrgID\":{\"FNumber\":\"\"},\"FIsBorrow\":\"false\",\"FTOCONTACTUNITTYPE\":\"\",\"FTOCONTACTUNIT\":{\"FNumber\":\"\"},\"FBankName\":\"\",\"FBankAcctName\":\"\",\"FBankAccount\":\"\",\"FIsOnlineBankPay\":\"false\",\"FProvince\":{\"FNUMBER\":\"\"},\"FCostProductID\":{\"FNUMBER\":\"\"},\"FCITY\":{\"FNUMBER\":\"\"},\"FDISTRICT\":{\"FNUMBER\":\"\"},\"FOrgAmountSum\":0,\"FCheckedOrgAmountSum\":0,\"FLocCurrencyID\":{\"FNUMBER\":\"\"},\"FExchangeRate\":0,\"FExchangeTypeID\":{\"FNUMBER\":\"\"},\"FLocAmountSum\":0,\"FCheckedLocAmountSum\":0,\"FCreatorId\":{\"FUserID\":\"\"},\"FCreateDate\":\"1900-01-01\",\"FModifierId\":{\"FUserID\":\"\"},\"FModifyDate\":\"1900-01-01\",\"FAPPROVERID\":{\"FUserID\":\"\"},\"FAPPROVEDATE\":\"1900-01-01\",\"FRefundDate\":\"1900-01-01\",\"FPayBankID\":{\"FNUMBER\":\"\"},\"FBillTypeID\":{\"FNUMBER\":\"\"},\"FShowLocInfo\":\"false\",\"FBankAddress\":\"\",\"FBankCnaps\":\"\",\"FBankDetail\":{\"FNUMBER\":\"\"},\"FCountry\":\"\",\"FNProvince\":\"\",\"FNCity\":\"\",\"FNDistrict\":\"\",\"FFromTransfer\":\"false\",\"FBringAccount\":\"\",\"FEntity\":[{\"FEntryID\":0,\"FSourceBillType\":\"\",\"FSourceBillNo\":\"\",\"FSourceRowID\":0,\"FLocAmount\":0,\"FCheckedLocAmount\":0,\"FExpenseItemID\":{\"FNUMBER\":\"\"},\"FOrgAmount\":0,\"FCheckedOrgAmount\":0,\"FEntryCostDeptID\":{\"FNUMBER\":\"\"},\"FRemark\":\"\"}]}}";
//调用接口
var resultJson = client.Save(formId,jsonData);
//对返回结果进行解析和校验,这里使用的是JsonPatch
var resultJObject = JObject.Parse(resultJson);
var queryNode = resultJObject.SelectToken("$..IsSuccess");
//判断并记录结果
if (queryNode == null)
{
Info.AppendLine("返回异常");
}
else
{
var isSuccess = queryNode.Value<Boolean>();
Info.AppendLine(isSuccess ? "操作成功" : "操作失败");
}
上面代码中,client.Save(formId,jsonData) 为保存函数,第一个formId表头已经配置过,那么参数信息中,只需要配置一个参数就可以了,参数编号及名称自定义填写就可以,勾选“取值逻辑”,然后点击“逻辑设定”按钮,打开脚本编写窗口(标准js语法),组织jsonData参数,最终返回json结果,示例:
xxxxxxxxxx
//通过 dthead获取表头数据,通过dtbody获取表体数据
var jsonData = {};
jsonData.NeedUpDateFields = [];
jsonData.ValidateFlag = true;
/*
jsonData.属性名称 = 值;
...相关赋值逻辑
*/
return jsonData;//返回JSON对象
2、Web服务
填写 Web服务地址,如 http://***/websrv.asmx?wsdl ,填写Web服务中的方法名
“参数信息”中,组织Web服务方法所需要的参数,如果方法需要2个参数,则需要添加两行,参数的顺序要跟方法中的参数顺序一致,参数名自定义即可。
3、Web API
填写Web API的Url地址,请求方式可选择 GET或POST
“参数信息”中组织请求参数,有几个参数填写几行,其中参数名要跟web api的参数名保持一致。
4、程序集
建立c#类库项目,按照要求添加dll引用,按照“类型”提示信息,实现相应的接口,编译后拷贝到 根目录/bin(标准版)或者根目录下面(微云版)
如果选择的类型是“其他”,需实现Iplugin接口,接口定义如下
xxxxxxxxxx
public interface IPlugin
{
string ExcutePlugin(Seejee.DataCommon.DACommand cmd, params object[] args);
}
其中args参数为数组,“参数信息”中定义的所有参数都会传到args参数中,提供程序中调用访问。
5、U8
首先在 控制面板 -- 集成定义 -- 单据标准接口/凭证标准接口 中配置U8接口,然后在此处“U8 EAI”编号出填写上接口编号就可以。
6、U9
待完善
7、存储过程
填写“存储过程名称”,类型中,如果是单据标准存储过程接口部分,选择对应的接口类型即可,如果选择“其他”,则“参数信息”中定义的参数,都会作为存储过程参数传入到自定义存储过程中。
1、执行前存储过程
此存储过程会在插件正式执行前执行,存储过程参数: @code varchar(20)【接口编号】,@keyid varchar(60) 【数据源主键ID】
必须返回包括bignore字段的单行数据集,1表示继续插件执行,0表示跳过执行,一般用于避免一个接口被重复执行
2、执行后存储过程
此存储过程在插件执行完毕后执行,存储过程参数: @code varchar(20)【接口编号】,@keyid varchar(60) 【数据源主键ID】, @retkey varchar(200) 【接口返回值关键信息】接口调用成功后才会执行,并且在一个独立数据库事务中运行
一般在此处记录插件执行日志,因为在一个独立事务中,所以不会跟随回滚,比如调用接口生成一个单据后,将生成的单据ID记录到一个中间表,后续如果发生重复调用,在执行前存储过程判断跳过即可。
3、执行后逻辑
纯js的逻辑判断,比如调用金蝶云星空单据保存接口,接口返回值是一个json对象,如下:
执行后逻辑应该如下编写:
xxxxxxxxxx
//params.retData :构件返回值[字符串类型(格式:{bsuc:true/false,msg:"",result:"接口返回值"})],此处调用K3接口,即为K3保存后返回值(如上图)
var ret = JSON.parse(params.retData);//将Json字符串转为Json对象
//如果插件调用成功
if(ret.bsuc){
var pluginret = JSON.parse(ret.result);
if(pluginret.IsSuccess){
return {bsuc:true,msg:"",result:null}
}
else{
return {bsuc:false,msg:ret.ErrorCode,result:null}
}
}
else{
return params.retData
}
//说明:执行后逻辑最终都要返回一个{bsuc:true/fales,msg:"错误消息",result:null}格式的JSON返回值,如果成功 bsuc置为true,置为false时,系统会认为插件执行失败,前端抛出异常,如果是一个查询接口,result可以传入结果集数据组。
说明:执行后逻辑如需调试,须通过日志的形式进行查看,日志查看请参考下面的”脚本日志“章节。
4、插件测试
点击测试按钮,在“数据集主键值”中输入需要查询的数据集查询主键值,系统会显示执行日志,如下图
单据插件配置是将定义好的插件,在对应的单据标准接口上进行挂接,一个接口可以挂接多个插件,按照配置的顺序号执行对应的插件,插件执行如果有异常,则会回滚当前接口的事务,需注意的是,由于插件是调用的外部系统,如果连续调用多个插件,前面有执行成功的,是不会自动回滚的。需结合日志记录来在插件中判断是否需要执行插件。
如果执行的是标准接口插件,比如实现了一个保存后构件或者保存后存储过程,配置到了插件当中,那么插件的“类型”要跟此处的“动作”保持一致,这样的话标准的构件参数或者存储过程参数才会对应传到自定义构件和存储过程里面,否则只会使用插件中的“参数信息”中的参数。
逻辑设定中,使用的都是标准的js脚本,在脚本中允许访问数据集,数据集作为一个或多个参数可以在脚本中直接使用,参数名即为数据集编号,如插件对应的数据源中有一主一从两个数据集,编号分别为 sobillm 和 sobillc,如下图:
那么可以通过 “主数据集编号.字段名” 可以访问数据集字段值;通过 ”子数据集编号.length“可以访问子数据集行数,示例:
xxxxxxxxxx
var code = sobill.ccode;//访问主表字段值,主表查询结果的字段都可以通过此种方式访问
for(var i=0;i<sobillc.length;i++){
var rowdata = sobillc[i];//遍历子表行
var invcode = rowdata.cinvcode;
}
脚本日志用于查看或者跟踪自定义js脚本,包括 插件定义 中的参数信息中写的逻辑定义以及执行后逻辑
插件定义中点击“开始测试”时,详细脚本执行会记录在 平台根目录/logfile文件夹下最新的debug.log文件中,如下图所示
js代码中如需记录日志,用法如下
xxxxxxxxxx
log.Info("日志");//记录info日志
log.Debug("日志");//记录debug日志
log.Error("日志");//记录错误日志
示例
xxxxxxxxxx
var ret = JSON.parse(params.retData) ;
log.Debug(params)
return {bsuc:true,msg:"",result:"aaa"};
日志
如何强制开启日志
浏览器打开如下地址:
1、微云版: http://平台地址/plat/pluginlog
2、标准版:http://平台地址/sysplat/dataget/data.ashx?type=pluginlog
提供了2中方式的插件调用:
1、跟随单据的标准接口调用,如单据的保存前、保存后、提交前、节点审批等标准事件中。
2、独立调用,通过url地址进行调用,此种方式适合在前端做一些数据的接口查询
标准版:http://平台地址/sysplat/dataget/data.ashx?type=plugin&code=插件编号&key=数据集主键值&test=0,test代表是否测试状态,取值1/0
标准版:http://平台地址/plat/plugin?code=插件编号&key=数据集主键值&test=0,test代表是否测试状态,取值1/0
添加CommonClass.dll引用
1、创建类库项目,实现ILogin接口
xxxxxxxxxx
using BSFrameSolution.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyLogin
{
public class Class1 : BSFrameSolution.Common.ILogin
{
//登录时校验逻辑
public bool DoLogin(string usercode, string pwd, params string[] args)
{
return true;
}
//登陆信息丢失,点击重新登陆时校验逻辑
public bool ReLogin(string usercode, string pwdflag, params string[] args)
{
//通过CommonClass.EncryptHelper.Sha1(CommonClass.EncryptHelper.Md5(CommonClass.EncryptHelper.Md5(原始密码)))加密后域pwdflag比较是否一致
return false;
}
}
}
2、编译的MyLogin.dll 拷贝至平台bin目录
3、修改web.config
xxxxxxxxxx
<appSettings>
<add key="loginplug" value="MyLogin.Class1,MyLogin"/>
</appSettings/>
添加Focus.Common.dll引用
1、创建类库项目,实现ILogin接口
xxxxxxxxxx
using Focus.Common;
namespace MyWebAPI
{
public class Class2 : ILogin
{
//登录时校验逻辑
public bool DoLogin(string usercode, string pwd, params string[] args)
{
return true;
}
//登陆信息丢失,点击重新登陆时校验逻辑
public bool ReLogin(string usercode, string pwdflag, params string[] args)
{
//通过Focus.Common.EncryptHelper.Sha1(Focus.Common.AESCryptoHelper.Encrypt(Focus.Common.Md5Helper.CreateMd5Str("原始密码").ToLower()))加密后域pwdflag比较是否一致
return true;
}
}
}
2、编译的MyWebAPI.dll 拷贝至平台bin目录
3、修改appsettings.json
xxxxxxxxxx
"Loginplug": "MyWebAPI.Class2,MyWebAPI.dll",//根节点下添加
xxxxxxxxxx
public interface ItestService
{
string test();
}
xxxxxxxxxx
public class TestServcice : ItestService
{
public string test()
{
return "测试依赖注入1";
}
}
详见“如何自定义扩展WebAPI”章节中的“其他注意点”
xxxxxxxxxx
"UserSrv": "ServiceTest" //多个dll用逗号隔开
自定义web请求是指基于平台本身已经提供的标准请求之外,新增个性化的其他自定义web请求,扩展方式如下:
1、标准版(Framework) ,参考 ”如何自定义Web请求“ 章节。
2、微云版(core),参考 ”如何自定义扩展WebAPI“ 章节。
本功能适用于聚恒中台标准版
1、新建一个自定义类,并继承类FocusBaseRequest ( 位于CommonClass.dll ),重写 DoRequest方法
xxxxxxxxxx
namespace MyWebRequest
{
public class OrderReq: FocusBaseRequest
{
/// <summary>
/// 重写方法
/// </summary>
/// <param name="cmd">数据库访问类</param>
/// <param name="context">请求对象,获取post或get数据</param>
/// <returns></returns>
public override async Task<string> DoRequest(Seejee.DataCommon.DACommand cmd,System.Web.HttpContext context){
string stroptype = context.Request.QueryString["optype"];//自定义的请求标识,区分不同的业务处理
if(stroptype == "getorder"){//获取订单数据
var data = await GetOrderData();
return Newtonsoft.Json.JsonConvert.SerializeObject(data);//返回处理结果
}
}
}
}
2、编译类库,生成dll,如 MyWebRequest.dll
3、拷贝dll至平台bin目录下
4、配置请求路径
打开表bs_customrequest,新增一个请求路径,如下
id(主键) | ckeyrequest(请求关键字) | cassembly(程序集名称) | cclassname(类全名) | bsystem(系统预置) |
---|---|---|---|---|
1 | myorder | MyWebRequest | MyWebRequest.OrderReq | 0 |
5、web请求方式
url地址:http(s)://平台地址/sysplat/dataget/data.ashx?type=myorder&optype=getorderdata
说明:
a) 通过type=myorder来匹配 bs_customrequest中定义的请求关键字,如果符合会实例化MyWebRequest.OrderReq类
b)通过optype=getorderdata 来作为DoRequest方法中的请求标识,不同的请求标识进入不同的处理逻辑,最终返回处理结果
本功能适用于聚恒中台微云版
1、添加Microsoft.AspNetCore.Authentication.JwtBearer.dll,Focus.DBFactory.dll,Focus.Common.dll ,Focus.Logger.dll,Focus.Cache.dll 引用(位于平台根目录)
2、为了能够使用Web特性,在工程配置文件中修改配置,如下:
a、
b、修改项目输出类型为类库
1、继承类
类名以Controller结尾,如 homeController
如果不需要鉴权,则继承Microsoft.AspNetCore.Mvc.Controller 类
如果需要鉴权,则集成 Focus.Common.FocusWebAPIController类
xxxxxxxxxx
namespace MyWebAPI
{
public class MyTestController: Microsoft.AspNetCore.Mvc.Controller
{
//日志类对象
private ILog log
{
get
{
return Focus.Logger.LoggerManager.GetLogger<MyTestController>();
}
}
//redis缓存类对象
private RedisCacheHelper redisCache
{
get
{
return Focus.Cache.RedisCacheHelper.Instance;
}
}
Focus.DBFactory.FocusDBCommand _cmd;
//依赖注入,自动带入数据库访问类对象cmd, hostingEnvironment:服务器环境对象,可获取程序路径信息,configure:配置文件类,访问appsettings.json内容
public MyTestController(Focus.DBFactory.FocusDBCommand cmd, IWebHostEnvironment hostingEnvironment, IConfiguration configure)
{
_cmd = cmd;
}
[HttpGet] //自定义api请求函数
public string Test(string str)
{
return "MyTestController:" + str;
}
public static void Main()
{
}
}
}
说明:如果需要鉴权,则在客户端需要先获取token,然后请求webapi时,在header中携带token
获取token方法:
URL:http://IP地址:端口号/token/get?username=用户名&password=md5加密后的密码
例如:http://localhost:8877/token/get?username=admin&password=d9f6e636e369552839e7bb8057aeb8da
返回值:{"token":"F9JetsbLCNnfqNAFgvVpQO7o1YWtSJ6dRCOyW7Z7mnONjjj///+gLw4sKExL77v8LPNqd1+T/gkbZiwe2vueHcMnfBM7eg0s9RRHzEdo/laPukCelBd1dBQ4NWZdBpiZvDkEOzP9WtpuqLy0644YMwvuwMfM6Wxi1Tw4zAOPPmN1pslWIsPJKA3yRITfBcsyWeSW2Y+grCcA52eH4kQ07z6nVT0ShDFLJ2Ca/JmOO0saPevzDyp8GBlHeDSWw462B4bHpAbHSLGG7+iHbiDhxDGIesRFLfAbHDHplS56wuWzTx2mdrP5l6yI5aJ/j4RECwqxAVJVj8gQl6h+o/YCG72NoqRa5uxQdEDg/vOk3bFHp2rjH06KkDFgsd6oahp69YCxUTpyM1VzcjgiWGZ38GeM+0W/2r4A8rUMhuDNas6fy7J1rvp2851PvpQZNJruQ4B0ZiPpPy9QvIVr/vzUZUM/GSdBwOPxDIy5cgD6ualfaR5zTfuey/2H30U3GKNzA78f9wYPHWWH0YmwvGrEVDIsx66rOEcHwJVNp4XNX512XJRoGsH5OFoTMSYbhWHMOKCizbk192u87/j2yZeKZf+bBom4YRcXXZgSICWy51Ycm1OsjfdOXPojDa0zGQACIHc1bWaqA1aqM35I5SW9tZz8x8QCFWybfFT7vLEkRGRPXMEOFq+JSWtQDC9pUWJGdNhM9g/pvPjQegNScJfy7Lb1a7cuYxS+eSMijfi+1VvU+5BV+Agvmki3yN6imtUd7vx9qKgbRwP/eMsYmukDC2AncyExwEGEL5nKhANumWN1Zt2rCox+eo7+72/6kZZI6KQIIGsf/GUadiTAqr1FfcMrUp0kDC2KwPpCaRcpr/H69lm6lUAsNM+2AmvFKUB3SxG4+n6R/5jg6u/ZDs4gLSYnzMQKuUBhE/B7Rrsmq8PR5T6PHXF6MjsmLH0Z7odfJC/zlBF7Rm4x2EHAv7jRvgcNhEkK8wNxzkeIdE36TENmceNQxWIuNz+TE5VrfGpwysA7GgLHrSzhxSRICEFk7TulcS1QL+lrEdr2q8m/F0fPskYGUgL/O2Yfslx2g2+oro7L9FTSCr/lmBNnpJ9LoSyt7PTyKzfYiznztstwILCI1hpC1HbcOEJGP9CtnFZvVhNJ9ua5FRl0XxojVXEYqe9s87WebaaVGgs7kdUXlad/xkdyqBq4iaBzIYd463yafnkKQxfrTO6+duyrecgSg0NXzSyfRL/vfZgEiThW5tY=","errorCode":"","msg":""}
2、配置依赖注入项
在类的入口方法中配置,如果不需要处理数据库,可以删掉入口函数
4、编写自定义API方法
方法最好使用示例中的异步方法。
可以通过 http://ip:端口/MyTest/test地址进行测试
如果在平台内部脚本访问,通过this.getRemotData 函数,比如
xxxxxxxxxx
this.getRemotData("MyTest/test","get",{},function(str){
console.log(str)
})
如果需要获取当前登录信息,通过如下代码:
xxxxxxxxxx
Focus.Common.AppHttpContext.loginInfo
打开平台根目录 appsettings.json,在“UserAPI”配置节中填写dll文件名称,多个用逗号隔开
双击FocusApp.exe启动服务
先进行登录验证,验证通过后跳转至相应的地址
登陆验证
返回值:
xxxxxxxxxx
{bsuc:true/false,msg:"错误说明",info:{}}
地址跳转
xxxxxxxxxx
location.href = "http://*"
示例:
xxxxxxxxxx
this.login("用户名","密码",function(ret){
if(ret.bsuc){
location.href = "...";
}
})
注意事项:
单点登录密码一般最好单独设置,在“用户管理“界面的”单点登录密码“字段;通过单点登录函数和token获取时,都支持使用单点登录密码。
地址:http://平台地址/sysplat/dataget/data.ashx?type=srv_flowcommit&vo=ws_bil&key=646b9cb2-e71c-49fb-97ae-1cca6ccf2ce7&comtype=1&formid=&usercode=admin&userpwd=d9f6e636e369552839e7bb8057aeb8da
参数说明:
参数名 | 参数说明 | 其他说明 |
---|---|---|
type | 固定值:srv_flowcommit | |
vo | 单据类型 | |
key | 单据ID | |
comtype | 1:提交 0:取消提交 | |
formid | 如果是新版单据ID,为空则为老版单据 | |
usercode | 用户编号 | |
userpwd | 审批人登录密码(对应pb_operator的cjwtpwd字段值 |
返回值
xxxxxxxxxx
{"bsuc":true,"msg":"提交成功","result":{},"result1":{}}
地址:http://平台地址/sysplat/dataget/data.ashx?type=srv_flownodevery&usercode=demo&userpwd=123&version=version&nodeid=nodeid&memo=memo
参数说明:
参数名 | 参数说明 | 其他说明 |
---|---|---|
type | 固定值:srv_flownodevery | |
usercode | 审批人编号 | |
userpwd | 审批人登录密码(对应pb_operator的cjwtpwd字段值 | |
version | 审批实例版本号 | |
nodeid | 审批节点id (对应bs_flowdetail的cnodeid) | |
memo | 审批意见 |
返回值
xxxxxxxxxx
{"bsuc":true,"msg":"","result":{},"result1":{}}
平台对外接口提供第三方调用,以WebAPI的方式提供,包括单据保存接口、单据删除接口、获取单据数据接口、提交审批流接口,
调用接口的步骤分为两步:
1、根据用户名和密码获取token
2、携带token 发起调用请求
3、接口日志:logfile/日期_info.log
请求地址:http://平台地址/token/get?username=admin&password=123
参数说明
参数名 | 说明 | 是否为空 |
---|---|---|
username | 用户名 | 否 |
password | 密码,md5加密后的原始密码或者jwt密码(pb_operator中的coperpwd或cjwtpwd) | 否 |
返回值格式
xxxxxxxxxx
{
"token":"F9JetsbLCNnfqNAFgvVpQO7o1YWtSJ6dRCOyW7Z7mnONjjj///+gLw4sKExL77v8LPNqd1+T/gkbZiwe2vueHcMnfBM7eg0s9RRHzEdo/laPukCelBd1dBQ4NWZdBpiZvDkEOzP9WtpuqLy0644YMwvuwMfM6Wxi1Tw4zAOPPmN1pslWIsPJKA3yRITfBcsyWeSW2Y+grCcA52eH4kQ07z6nVT0ShDFLJ2Ca/JmOO0saPevzDyp8GBlHeDSWw462B4bHpAbHSLGG7+iHbiDhxDGIesRFLfAbHDHplS56wuWzTx2mdrP5l6yI5aJ/j4RECwqxAVJVj8gQl6h+o/YCG72NoqRa5uxQdEDg/vOk3bFHp2rjH06KkDFgsd6oahp69YCxUTpyM1VzcjgiWGZ38GeM+0W/2r4A8rUMhuDNas6fy7J1rvp2851PvpQZNJruQ4B0ZiPpPy9QvIVr/vzUZUM/GSdBwOPxDIy5cgD6ualfaR5zTfuey/2H30U3GKNzA78f9wYPHWWH0YmwvGrEVDIsx66rOEcHwJVNp4XNX512XJRoGsH5OFoTMSYbhWHMbI9QGKpKUZvDkECfCwv/HlEiCbe3oqF062HE3bnS2UXEGLnTrUTGb+Zox5PYDXCEIHc1bWaqA1aqM35I5SW9tZz8x8QCFWybfFT7vLEkRGRPXMEOFq+JSWtQDC9pUWJGdNhM9g/pvPjQegNScJfy7Lb1a7cuYxS+eSMijfi+1VvU+5BV+Agvmki3yN6imtUd7vx9qKgbRwP/eMsYmukDC2AncyExwEGEL5nKhANumWN1Zt2rCox+eo7+72/6kZZI6KQIIGsf/GUadiTAqr1FfeoHKApolQC9whmQAA2ibE84WtoR4HmcqlYrD2URA01hyWQIHUVmNPojzftHIKp1Ilc6uMJXBDoTBeTmeX9+vbWBGWkvwwMYbittjDoHZJmUhWM3Xko8G2raH+BzJERxd/gr4lW/OX+lYwEQbz2jzr61DN7FEztk+dU7x5KqOMFMxNmkSYWUKBg9defYQoBbRR2bOKknjHN3Audaq8RnIA30i605q5ctnfBGOBV6HgayT/PdnXCYUbb3o05mJ5GZkklOwLp2VVTpVhp1Kw93mvJiHiF1o/zMfxWC6sLNa6kacZ6AiqBgJNDodlsWB+0Ias0h1Mv3N3iANOF0gX3wf3vGyrMroEifJRWF5iZUqul0Tg1NSl8tf0ibf7PuTTKfrsCrsk4C0xtWh0TOUMVKbBf4SV/RE237sCKN76fbwIGrFLJTWNL3nP1j9cl6FkdxfA==",
"errorCode":"",
"msg":""
}
特殊说明:如果是在平台内部通过脚本调用,则不需要获取token这一步骤。
PostMan示例
请求地址:http://平台地址/api/open/commit
请求方式:post
提交方式:
form参数
参数名 | 参数值 |
---|---|
voucherno | 单据类型编号 |
keyid | 单据ID |
formid | 单据对应的窗体ID |
flowcode | 审批流编号,一般同voucherno |
示例:
xxxxxxxxxx
//Voucherno:单据类型 KeyID:单据ID FlowCode:审批流编号 FormID:单据窗体ID
let data = {Voucherno:"pu_bill",KeyID:"0001857D-8F31-4B5D-B0FF-FEC74E8C4679",FlowCode:"pu_bill",FormID:""}
$.ajax({
url: "",
type: 'post',
//已登录的Form窗体中可以省略beforeSend函数
beforeSend: function (request) {
request.setRequestHeader("Authorization", token1/*得到的token*/);
},
data : {'data':data},//form参数
contentType:'application/x-www-form-urlencoded; charset=UTF-8',
dataType: 'json',
timeout: 30000,
success: function(){},
error: function(){},
complete: function(){}
})
设置token
配置form参数
请求地址:http://平台地址/api/open/approve
请求方式:post
form参数
参数名 | 参数值 |
---|---|
voucherno | 单据类型编号 |
keyid | 单据ID |
formid | 单据对应的窗体ID |
nodeid | 审批节点ID |
memo | 审批意见 |
ispass | 审批状态(1-通过 0-驳回) |
示例:
xxxxxxxxxx
//Voucherno:单据类型 KeyID:单据ID FlowCode:审批流编号 FormID:单据窗体ID
let data = {Voucherno:"pu_bill",KeyID:"0001857D-8F31-4B5D-B0FF-FEC74E8C4679",FormID:"窗体ID",NodeID:"节点ID",memo:"同意",ispass:"1"}
$.ajax({
url: "",
type: 'post',
//已登录的Form窗体中可以省略beforeSend函数
beforeSend: function (request) {
request.setRequestHeader("Authorization", token1/*得到的token*/);
},
data : {'data':data},//form参数
contentType:'application/x-www-form-urlencoded; charset=UTF-8',
dataType: 'json',
timeout: 30000,
success: function(){},
error: function(){},
complete: function(){}
})
PostMan示例:
设置token
配置form参数
请求地址:http://平台地址/api/open/get
请求方式:get
参数说明
参数名 | 说明 | 是否为空 |
---|---|---|
voucherno | 单据类型 | 否 |
keyid | 单据ID | 否 |
示例:
http://localhost:51249/api/open/get?voucherno=pu_bill&keyid=18abc39e-dc30-4233-800c-a646ab3c0eab
返回值:
xxxxxxxxxx
{"bsuc":true,"msg":"","result":{"vi_pu_billm":[{"id":"18abc39e-dc30-4233-800c-a646ab3c0eab","dcreatetime":"2022-08-11T17:36:09","dmodifytime":null,"bsysdel":null,"ccode":"202208001","ccommit":"","ccorpcode":"010101","cdepcode":"010101030101","cmaker":"超级管理员","cmakercode":"admin","cmemo":"","cposcode":"01","cstate":"","ctypecode":"132903120201578274","cversion":"","cvery":"","dcommit":null,"ddate":"2022-08-11T00:00:00","dtime":"2022-08-11T09:36:07.727","dverydate":null}],"pu_billc":[]},"result1":{}}
请求地址:http://平台地址/api/open/save
token传值方式:参考单据数据查询接口
请求方式:post
参数说明
参数名 | 说明 | 是否为空 |
---|---|---|
data | 单据数据 | 否 |
data数据格式:
xxxxxxxxxx
{
"head":{"字段1":"值1","字段2":"值2","字段3":"值3"},
"body":{
"表体1别名":[{"字段1":"值1","字段2":"值2","字段3":"值3"},{"字段1":"值1","字段2":"值2","字段3":"值3"}]},
"表体2别名":[{"字段1":"值1","字段2":"值2","字段3":"值3"},{"字段1":"值1","字段2":"值2","字段3":"值3"}]},
}
示例:
xxxxxxxxxx
let data={head:{},body:{},voucherno:"demo01"}
data.head = {ddate:this.getDate(new Date()),ctypecode:"132294147206505313",cmaker:"admin",cmakercode:"admin"}
data.body["demo1c"] = [{a:"a1"},{a:"b1"}]
this.getRemotData("api/open/save","post",{data:JSON.stringify(data)},function(ret){
console.log(ret)
},
function error(err){
console.log(err)
})
请求地址:http://平台地址/api/open/delete
token传值方式:参考单据数据查询接口
请求方式:post
参数说明
参数名 | 说明 | 是否为空 |
---|---|---|
data | 单据数据 | 否 |
data数据格式:
xxxxxxxxxx
{voucherno:"",keyid:""}
示例
xxxxxxxxxx
let data={voucherno:"demo01",keyid:"123"}
this.getRemotData("api/open/delete","post",{data:JSON.stringify(data)},function(ret){
console.log(ret)
},
function error(err){
console.log(err)
})
数据集成管理平台支持基于不同异构数据源的数据抽取、转换、清洗、汇总、排序,经过一系列的数据处理,将最终数据输出到数据仓库或者excel文件,或者形成一个Json结果集来提供前端使用。
数据集成管理平台分为ETL任务流程设计和任务编排设计两部分。ETL任务流程设计允许你通过可视化流程处理节点来进行数据的抽取转换定义及输入输出定义;任务编排设计允许你以每个ETL任务为节点,可视化进行任务的执行流程定义。
数据集成管理服务以微服务的形式存在,可以配置与微云版或者标准版平台进行关联。
微服务名称:Focus.DataCore
appsettings.json配置
1、配置端口号,如 6001
2、配置应用服务信息 HostUrl和HostType
参数 | 说明 | 示例 |
---|---|---|
HostType | 应用服务类型 | framework(标准版)、core(微云版) |
HostUrl | 平台应用服务地址 | 如http://localhost/plat 或者 http://localhost:8877 |
双击Focus.DataCore.exe 启动服务
1、微云版:修改appsettings.json文件,在RestAPI下添加配置节
xxxxxxxxxx
"RestAPI": {
"OcrAPI": "",
"MailAPI": "http://localhost:8083",
"PushAPI": "http://121.199.23.183:8091",
"DataCoreAPI": "https://localhost:5001" /*数据集成微服务地址*/
},
2、标准版:打开web.config,在appSettings下添加配置节
xxxxxxxxxx
<add key="datacoreapi" value="http://localhost:6001" />
1、标准版
地址:http://平台地址/sysplat/dataget/data.ashx?type=datacoretask_run&taskcode=ETL任务编号
2、微云版
地址:http://平台地址/plat/DataCoreTask_Run?taskcode=ETL任务编号
1、标准版
地址:http://平台地址/sysplat/dataget/data.ashx?type=datacoreflow_run&flowcode=流程编排编号
2、微云版
地址:http://平台地址/plat/datacoreflow_run?flowcode=流程编排编号
ETL流程中,Excel/csv输入和Excel/csv输出节点中,文件统一通过应用服务进行存取
1、Excel/CSV输入,在 bs_excelfile表中插入新增的Excel文件信息,包括文件名及保存路径,bread字段默认为0,cextend字段填写 excel和csv二选一, cclass为自定义值,可以再输入节点中按照“分类编号”或者“文件名”来获取指定文件
2、Excel/CS输出,数据集成微服务在计算流程后,如果有excel输出节点,系统会将文件自动上传到应用程序目录:位于 根目录/wwwroot/datacorefiles文件夹(微云版),根目录/datacorefiles文件夹(标准版),同时会在表bs_excelout中生成文件信息,如下图
bs_excelout字段说明(Excel/CSV输出表)
字段 | 说明 | |
---|---|---|
cfilename | 原始文件名 | |
cfilename1 | 文件夹中的文件名 | |
ddate | 生成日期 | |
cprocode | ETL流程编号 | |
cversion | ETL流程计算实例ID,对应bs_datatask实例计算表 |
bs_datatask字段说明(ETL计算实例表)
字段 | 说明 | |
---|---|---|
cprocode | ETL流程编号 | |
cversion | ETL流程计算实例ID | |
dbegin | 计算开始时间 | |
dend | 计算完成时间 | |
cexception | 计算错误信息 | |
bsuc | 计算成功标志 |
webapi接口平台提供外部系统的api接口调用,接口为标准的restful接口,将数据资产通过接口的形式对外提供服务。
Web API接口平台通过独立的web应用程序对外提供服务,不依赖于FocussApp应用服务。
1、定义接口端点和用户,端点是一种分组的概念,比如为不同的系统调用指定不同的端点,端点下面建立用户信息并设定用户密码
2、定义接口,接口分为三大类:(a) 平台模型级(包括数据模型和数据列表)、(b) 数据库操作级(增删改查)、(c)构件级(自定义webapi构件)
1、添加端点
端点即用户分组,方便用户的管理,比如分别为HR系统和CRM系统建立两个端点,两个系统的接口用户分别建在对应端点下面
密钥为自定义的一个字符串,建议具备一定的复杂度
2、添加用户
选择一个端点,新加用户,用户必须从平台的操作员中选择,密码单独指定,API用户的密码与平台操作员登录密码各自独立,用户编号共享。
api接口管理用户自定义不同类型的web api接口,可以灵活的进行停用启用以及设定权限验证
接口分为:单据查询、单据保存、单据删除、数据列表、自定义查询、数据更新、数据删除、自定义构件、树形结构几种
接口定义时,系统会自动生成调用示例代码和返回值格式,如上图。
接口类型 | 说明 | 关键填写项 | 请求方式 | 权限验证 |
---|---|---|---|---|
单据查询 | 平台单据对外查询接口 | 数据模型、路由 | 可选 | 可选 |
单据保存 | 平台单据对外保存接口 | 数据模型、路由 | post | 验证 |
单据删除 | 平台单据对外删除接口 | 数据模型、路由 | post | 验证 |
数据列表 | 平台列表对外查询接口 | 数据列表、路由 | 可选 | 可选 |
自定义查询 | 通过sql语句提供对外查询接口 | 路由、SQL语句、请求参数 | 可选 | 可选 |
数据更新 | 通过update语句提供更新数据服务 | 路由、SQL语句、请求参数 | 可选 | 验证 |
数据删除 | 通过delete语句提供更新数据服务 | 路由、SQL语句、请求参数 | 可选 | 验证 |
树型结构 | 通过sql语句返回一个树形结构的json | 路由、SQL语句、请求参数 | 可选 | 可选 |
自定义构件 | 通过C#编写webapi控制器,对外提供服务 | 路由 | 可选 | 可选 |
具体每种接口需要组织什么格式的参数,请参考自动生成的请求示例代码。
1、bs_openapilog 记录每个接口的调用信息,包括日期、调用人、接口ID等
2、bs_openapilog1记录异常接口调用或者请求处理超长的接口调用信息,ctype :error(错误) overtime(超长响应)
webapi作为独立的微服务应用,可以单独部署到独立的服务器,也可以部署为集群应用,部署方式如下:
架构示意图
从上图可以看出,标蓝色的即为需要部署的服务
网关服务:总服务端口,通过网关服务统一对外提供授权服务和api服务
授权服务:根据定义的授权用户信息请求授权服务进行权限的验证和授权信息的获取
api服务:web api应用服务,自定义的所有的webapi统一通过这个服务进行处理
配置服务:用于记录全局的配置信息,比如数据库连接信息、缓存配置信息、端口号配置信息等,授权服务和api服务在启动时,自动从配置服务上获取相应的配置信息,从而避免了为每一个微服务单独进行信息的配置(appsettings.json配置信息)
打开appsettings.json文件,分别配置授权中心地址和数据库连接信息,如下图
配置中心地址是共享给webapi服务使用,web api在处理请求时,会自动根据这个地址请求授权服务进行权限验证
数据库连接信息是给所有服务使用,授权服务和webapi服务都会根据配置信息连接相应的数据库
启动参数是给配置服务自己使用,指定配置服务的端口号
配置完成后,双击Focus.ConfCenterAPI.exe启动配置服务,本示例指定端口号:8001,如下图
打开appsettings.json,配置启动参数和配置中心地址,如下图
配置中心地址即为第一步打开的配置中心服务地址 http://localhost:8001/,如下图
xxxxxxxxxx
"confcenter": {
"configServer": "http://localhost:8001/",
"keys": "DbOption" //指定从配置服务appsettings.json文件中读取哪个配置节,此处只读取数据库连接配置
},
双击Fous.IDServer.exe启动授权中心服务,此示例中端口号为7001
打开appsettings.json,配置启动参数和配置中心地址,如下图
从配置服务中读取 DbOption和IdServer(授权服务地址)两个配置节
双击Focus.OpenAPI.exe启动服务,本示例 webapi应用服务的端口号设定为8877
4、网关服务(Focus.GateWay)
以上3个步骤分别配置好了配置服务、授权服务、webapi服务,由于每个服务都有一个独立的ip地址和端口号,如果直接暴露给外部进行调用,存在比较大的安全隐患,同时多个地址和端口在开发调用时,也会比较混乱,网关服务则是把不同的内部请求地址变为对外统一的请求地址和端口,通过不同的路由来区分服务即可。
打开OcelotConfig.json文件配置路由信息
配置启动端口信息,打开appsettings.json
双击 Focus.GateWay.exe 启动网关服务,如下图
完成最终部署
通过网关路由配置,我们可以得知,通过http://localhost:9001/auth/.... 这个路由,就会请求端口号为7001的授权服务,授权请求方式如下
通过http://localhost:9001/focusapi/.... 这个路由,就会请求端口号为8877的web api服务,如下示例
webapi定义:
PostMan请求:
修改 appsettings.json 文件,如下图
IsHttps:是否启用SSL证书,必填
CertName:证书名称(证书文件必须放在程序根目录,IsHttps为true必填)
CertPwd:证书密码(IsHttps为true必填)
ServerIP:应用IP地址,填写*代表任意IP
ServerPort:端口号,必填
两种方式启动:
1、双击FocusApp.exe
2、通过命令行 dotnet FocusApp.dll
日志文件默认存放在根目录 logfile文件夹下,按照日期存储。
系统日志分为四部分:普通信息日志、警告信息日志、错误信息日志、调试信息日志
1、普通日志 *_info.log:
微云版(core):appsettings中设置节点AppInfo-ActionLog 为true
xxxxxxxxxx
"AppInfo": {
"Debug": false,//调试模式,系统记录每次请求的参数和返回结果
"ActionLog": true //记录每次请求的概要日志以及请求响应时间
}
标准版(framework):增加配置节
xxxxxxxxxx
<add key="actionlog" value="1"/>
系统记录每次请求的概要信息以及耗时
2、警告日志 *_warn.log:系统中警告信息集中存储位置,一般记录处理时长超过3秒的请求信息。
3、错误日志 *_err.log:系统所有异常信息的存储位置
4、调试日志 *_debug.log:
微云版(core):appsettings中设置节点AppInfo-Debug为true
xxxxxxxxxx
"AppInfo": {
"Debug": true,//调试模式,系统记录每次请求的参数和返回结果
"ActionLog": true //记录每次请求的概要日志以及请求响应时间
}
标准版(framework):增加配置节
xxxxxxxxxx
<add key="isdebug" value="1"/>
一般在调试系统时启用,调试完毕后要及时关闭,系统记录每次请求的参数信息以及请求的返回结果。
其他说明:
如果有存在超过3秒的长时请求,系统会在控制台实时显示预警,同时会记录警告日志。
审批中执行构件或者存储过程的日志记录,日志文件位置
微云版:logflow文件夹下
标准版:focuslogs文件夹下,web.config 把flowlog设置为1
微信或钉钉在后台调用接口的日志记录,通过日志可以判断接口配置问题,日志文件位置
微云版:通过 httpp://IP地址/plat/wxlog 打开日志记录,日志文件位于 logfile文件夹下
标准版:web.config中,设置msglog项为1,日志文件位于 根目录\msglog_wx.txt 及 msglog.txt
平台App推送消息日志记录,通过日志可以判断接口配置问题,日志文件位置
微云版:logfile文件夹下,文件名 日期_info.log,需在appsettings.json文件将 AppInfo-AppmsgLog 配置为true
标准版:web.config中,设置msglog项为1,日志文件位于 根目录\focuslogs\msgpush日期.txt
只对.net core版有效
url地址:
xxxxxxxxxx
http://平台路径/plat/dolog?type=1
type为1时为开启跟踪,为0关闭跟踪
为防止开启后忘记关闭,导致性能下降,系统开启跟踪十分钟后会自动关闭,需要手动再次开启
sql跟踪结果路径:[平台程序根目录/sqllogfile] 文件夹