> 工具库功能和使用手册 [牧语 开发文档](https://aliwork.zitoo.com.cn/mjs.html)
mjs 配置型文档
---
## 宜搭公式
### 常用公式
1. 日期格式为数值, 如 7 - 12 点不允许点餐:
```
OR(GT(VALUE(TEXT(DATE(TIMESTAMP(SYSTIME())),"HHmm")),1600),GT(700,VALUE(TEXT(DATE(TIMESTAMP(SYSTIME())),"HHmm"))))
```
2. 获取天数差值:`DYAS(DATE(结束日期组件),DATE(开始日期组件))`
3. 由时间戳生成编号 `CONCATENATE("PRO",LEFT(TIMESTAMP(SYSTIME()),10))`, `CONCATENATE("COC",TEXT(TODAY(),"yyyyMMddHHmmSS"))`
4. 日期组件默认值:`TIMESTAMP(SYSTIME())`
5. 数据关联两个字段, 数据联动处理, 获取拼接字段的其中一个值:`ARRAYGET(SPLIT( 合同编号 ,"-"), 1)`
6. 明细数据某一列不能重复验证: 自定义验证函数 - 不能使用 scope, 移到 change 事件,通过 forceValid 实现
```
function validateRule(value, state, ctx) {
if (value == "自定义回款") return true
const details = ctx.store.getData("tableField_kexuivc3").fieldData.value
const arr = details.filter(detail => detail["selectField_kexuivc5"].fieldData.value == value)
return arr.length < 2
}
```
7. 映射人员搜索框人员信息: `USERFIELD(人员搜索框,"userId")` -- `name`; 获取部门: `DEPTNAME(人员搜索框)`
### 高级公式
- 使用高级公式,条件如涉及到当期表,则条件的首参必须是目标表字段:否则报:主条件参数错误
- 高级公式 crud:可执行明细插入明细,在关联审批流时。不能同时操作多个明细,或明细和明细外组件
---
## 2.表间数据关联
从一个表单或者流程页面,自动加载其它表单或者流程的明细数据,自动填充到当前页面指定组件内
### 前言
两个准备工作,整个应用仅仅配置一次。**上线前请切换生产地址,通过数据源配置方案仅修改一次即可**
- 提供应用的 code 和 secret:【应用设置】➜【应用数据】➜【应用编码】|【应用秘钥】
- 在数据源添加库地址,`mjs_LIB` 公共库地址:
### 效果
> [demo](https://www.aliwork.com/alibaba/web/APP_XZ180SDQ2TQ3UUB7KR2A/inst/formSubmit.html?formUuid=FORM-RM966M91I9MJ7RMQ0RKX6B6EN1I23L6ZOJNFKBA&corpid=dingebe19bcf228f85ec35c2f4657eb6378f&dd_addcookie=true) 页面
1. 场景:通过下拉条件关联目标单据值
2. 方案:支持多条件查询,支持查询后数据过滤,支持查询和明细赋值默认值:将 src 设置为空
3. 进阶:可实现一表查询更新多明细;多个明细来自多个数据源;主表更新主表:兼容一些当前页面需要公式或者数据联动情况【若需要作为联动条件,需要修复】;明细加载主表数据
4. 备注:取消所有组件的数据联动,若无异常可不取消 _【新的宜搭版本下拉修改优先级偶尔会高于 js 赋值,因此不取消可能会出现查询结果异常】_
### 参数
- 全局编码命名规则说明:
- cur 即 current,通常指当前页面内组件 ID;
- src 即 source,通常值源表页面内组件 ID;
- def 即 default,通常指默认值,默认值优先级低于来源表数据
- 一些必填字段情况说明:
- formId:来源数据的表单 Id,格式为 `FORM-xxxx`,可在应用设置或者直接打开目标页面,在地址栏粘贴
- conditions: 查询条件,格式为 ` [{ src: "", cur: "", def: "" }];`,def 优于 src, src 和 cur 均需要传入组件 ID,支持多条件查询
- funcFilter:明细集合数据过滤方法,若不需要过滤则置空。格式为 `detail => detail["compId"] == ""`
- 一些非必填选配字段说明:
- compId:报错的提示到输入框,若不传入则报错会以 `toast` 方式进行提示。报错提示在输入框,可阻断提交【建议配置】
- isForm:数据查询是否来自表单,流程数据通过表单方式亦能查询【通常不需要配置】
- errMsg:查询失败报错提示优先使用接口报错,若查询为空,默认提示为 `未查询到匹配数据`【通常不需要配置】
### 案例
> 打开页面的 `js` 功能,复制如下代码,**按需选择**
```
//---------------------- common ----------------------//
// 工具库: 库地址存储于 dp 在数据页面全局共享, dp 在页面加载后才有值
function loadAliworkSDK(ctx = window.LeGao.getContext()) {
const src = ctx.dp.getValue("mjs_LIB");
const script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute("src", src);
document.body.appendChild(script);
script.onload = () => _mjsOnload(ctx);
}
//---------------------- private ----------------------//
// 加载即调用方法请在此处进行调用
async function _mjsOnload(ctx) {
mjs.aliworkInitParams()
// 效验操作组权限:提交态
if (mjs._checkSubmitEnv()) { }
}
//【3.红色都是需要匹配的】明细查询和赋值
async function _queryAndUpdateCompData(ctx) {
//【3.1.多条件查询】表单查询条件:src 来源表条件组件Id,cur 当前表条件组件ID,def 默认值优先级低于来源表数据
const conditions = [{
src: "textField_kfnjmfq2",
cur: "selectField_kfnjp1cu",
def: ""
}];
const resList = await mjs._queryFormOrProcess(ctx, { formId: "FORM-JHYJNJSVPXGLZXSE43HQV9M8XE7U2UHXM00JKB3", /** 数据来源表Id */ conditions, compId_tip: "selectField_kfnjp1cu", /** 非必填:报错的提示到输入框,可阻断提交 */ })
//【3.2.1.多明细更新】明细组件Id映射表: src 来源明细表内组件ID,cur 当前明细表内组件ID,def 默认值优先级低于来源表数据
const props1 = [{
// 名称
src: "textField_kfnjmfq4",
cur: "selectField_kkny9ed5",
},];
mjs._updateCompDetailFromDetail(ctx, { resList, props: props1, compId_detail_src: "tableField_kfnjmfq3", /** 来源表明细组件Id */ compId_detail_cur: "tableField_kfnjmfq3" /** 当前明细的compId */ }, { funcFilter: null, /** 过滤方法条件:detail => detail["textField_kfnjmfq4"] == "条件" */ })
//【3.2.2.多明细更新】明细组件Id映射表: src 来源明细表内组件ID,cur 当前明细表内组件ID,def 默认值优先级低于来源表数据
const props2 = [{
// 名称
src: "textField_kfnjmfq4",
cur: "textField_kknt6uqk",
},];
mjs._updateCompDetailFromDetail(ctx, { resList, props: props2, compId_detail_src: "tableField_kfnjmfq3", /** 来源表明细组件Id */ compId_detail_cur: "tableField_kknt6uqo" /** 当前明细的compId */ }, { funcFilter: null, /** 过滤方法条件:detail => detail["compId"] == "条件" */ })
//【3.3.主表更新】组件Id映射表: src 来源表内组件ID,cur 当前表内组件ID,def 默认值优先级低于来源表数据
const mains1 = [{
// 主表赋值1
src: "textField_kfnjmfq2",
cur: "textField_kkoslg5n",
},];
mjs._updateCompMainFromMain(ctx, { resList, mains: mains1 })
//【3.4.明细加载主表】组件Id映射表: src 来源表内组件ID,cur 当前表内明细组件ID,def 默认值优先级低于来源表数据
const mains2 = [{
// 主表赋值1
src: "textField_kfnjmfq2",
cur: "textField_kkou7cb4",
},];
mjs._updateCompDetailFromMain(ctx, { resList, props: mains2, compId_detail_cur: "tableField_kkou7cb3" /** 当前明细的compId */ }, { funcFilter: null, /** 过滤方法条件:main => main["compId"] == "条件" */ })
}
//---------------------- event ----------------------//
// 【1.将事件绑定到页面】页面节点加载渲染完毕
export function didMount(ctx) {
// 加载工具库: dp & 初始化
loadAliworkSDK(ctx)
}
//---------------------- event ----------------------//
// 【2.将事件绑定到查询条件】当查询条件发生变更
export function onConditionChange(ctx) {
// 匹配查询和赋值
_queryAndUpdateCompData(ctx)
}
```
---
## 3.服务变更流程
服务可执行宜搭公有云接口文档所有服务,也可调用后台开发提供的接口服务
### 前言
若是单据页面,可直接提供高级公式执行更新操作。流程单据不支持高级公式,可通过服务来完成。变更发起者可以是流程也可以是单据
[demo](https://www.aliwork.com/alibaba/web/APP_XZ180SDQ2TQ3UUB7KR2A/admin/pageAppEdit.html?spm=a1z4e7.13467989.0.0.41821791ZVa3Wh#/form/process/FORM-JFYJZQEVMOVLZUIUVZE7VFJZON5P1B4L94QIK08/page?code=TPROC--JFYJZQEVMOVLZUIUVZE7VFJZON5P1B4L94QIK18&navUuid=FORM-JFYJZQEVMOVLZUIUVZE7VFJZON5P1B4L94QIK08&_k=hoakq3) 页面,**目前以流程实例更新为案例**
### 配置
- [接口地址:更新流程实例](https://www.yuque.com/yida-old/help/ihxi9r#r78nho)
- 将接口地址参数配置到宜搭服务,配置路径:【平台管理】➜【服务注册】➜【新增服务】
1. 类型选择:`GETWAY`
2. url 相对地址:填写对应服务的地址,如流程更新文档为 `/yida_vpc/process/updateInstance.json`
3. 参数列表:点击 _增加参数_ 将文档上参数一一对应拷贝过来
1. 注意每个参数填写完成点击右侧保存,点击确认保护不会自动保存
2. 第一列 _参数名_ 必须为文档参数名称,标签名(中文)可填写文档描述,标签名(英文)可填写参数名称
4. 其余默认即可:名称自行取,建议不要使用特殊符号,在使用的时候通过名称下拉选择
> 从接口参数可知,我们更新实例是需要 `实例 Id:processInstanceId 的`,因此需要一个中间表将 ID 存储起来
- 中间表:记录关键字段,如编号,审批状态,关联名称,实例 ID,余下主表数据可通过编号从原流程单据进行数据联动
- 审批过滤:此处刚好实现了审批过滤功能,一般将审批状态记录为三种状态:0-可提交;1-审批中;2-已通过;3-已关闭
> 数据引用,审批过滤:通过数据联动过滤审批状态,下拉框数据过滤审批状态;将实例 ID 字段通过中间表编号关联到出实例 ID 进行储存,配置服务都需要通过实例 ID 进行操作
- 原始表:添加公式在审批通过完成执行公式,将中间表关键信息进行写入
1. **注意:页面配置公式只对提交和编辑有效,执行公式不会触发页面内的组件公式,也不会自动带入默认值。**
2. **所有值的修改都需要写入,所有值的变动都需要写入**,因此此处要写入审批状态为 0【可提交】
- 中间表:将所有字段设置为可写入,将页面隐藏,方便管理员补录\调整数据。
1. 过滤逻辑为写入数据审批状态为 0,在被引用时过滤为 0 可选取
2. 当被下游单据引用时通过单号:在开始节点将审批状态更新为 1【审批中】。在完成节点,通过时将审批状态更新为 2 【已通过】,完成节点拒绝、撤销\终止,将审批状态回退为 0【可提交】。当单子行程闭环,可在适当位置将审批状态更新为 3 【已关闭】
- 变更表:建议将原始表 `Schema` 直接导入过来,删除不能变更的字段,或者保留,将变更字段设置为可编辑,其余为只读
1. 所有主表字段,通过数据联动完成,单号关联可来自中间表,可自行添加变更状态管理
2. 明细字段建议通过接口自动读取,详见**【1.表间数据关联】**;若无此条件,自行添加中间表处理,让用户执行下拉勾线联动
3. **明细字段**:服务对明细的操作是全量替换,为了简化操作,明细内的组件 ID _必须_ 要和原始表一致,方法可以是直接将原表通过 `Schema` 导入过来,也可以是直接复制原表明细组件,粘贴到变更表上
4. **明细变更**:添加一个多行输入框,将需要被修改的明细内组件设置为可操作,余下不可修改都为只读。将如下代码复制到 `js` 面板中,将需要变更的明细内组件设置添加 `onDetailChange `事件,传入 ctx,"当前明细组件 ID", "多行输入框 ID", 若传入 `details` 会优先使用不再去取明细组件值
```
// 用于明细更新后返回:兼容用户不修改情况下操作
mjs._assembleDetailForKeyValue(ctx, "", "textareaField_kis8c0n5", details)
// 用于明细内组件变更:兼容用户修改同步明细数据
mjs._assembleDetailForKeyValue(ctx, "tableField_kfnjmfq3", "textareaField_kis8c0n5")
```
5. 在流程完成节点配置服务:在同意节点执行服务操作,选择关联操作,选择第三方服务,此处会下拉出现在宜搭平配置的服务的名称列表,选择对应的服务,会出现配置好的参数列表,左侧显示名称为服务配置中文名称
1. userId:#{LOGINUSER} ➜ 取当前登录人;
2. 应用秘钥:在应用设置 ➜ 应用数据 ➜ 应用秘钥
3. 应用编码:在应用设置 ➜ 应用数据 ➜ 应用编码
4. 实例 ID:输入 # 号键后,会弹出当前页面所有字段选项,选择实例 ID 组件即可
5. 更新表单数据:`{"原始表组件ID":"#{当前表组件ID}","原始表组件ID":"#{当前表组件ID}"}`,一一对应,多个按照格式添加即可
6. **更新表单数据【明细处理】:**格式和 **5** 一致,_区别是当前明细组件 ID 不使用,使用格式化后的多行文本框的值,**尤其要注意:多行文本框不要添加不要引号,被转义后服务会失败**_,正确格式:`"原始明细组件ID": #{多行文本框ID}`
6. 服务可查询执行日志:
1. 日志地址:`https://aliwork.com/alibaba/web/APP_TYPE/query/invoke/queryRecord.json%3FpageNo=1&pageSize=10` ,将 `APP_TYPE` 替换为需要查询的应用 Id 即可
2. 查询方式:查询的应用是当前页面登录的企业宜搭账号,若不匹配会报无此应用
7. 补充:
1. 若需要更新的数据大,可使用 [json 格式化](https://www.bejson.com/),完成后复制到 `更新表单数据`
2. 操作建议:先配置更新一个主表字段,跑通流程,再测试多个字段,最后再单独测试明细数据
8. 三方服务
1. 宜搭服务回调是单向的, 没有重试机制, 所以流程审批结束同意操作下需要记录状态
2. 在页面内添加记录成功与失败状态的组件, 失败原因. 调用后端服务接口传入 #{formInstId}, 后台执行完成后调用宜搭更新接口会写执行状态
---
### 多选合并查询明细
[demo](https://www.aliwork.com/alibaba/web/APP_LJHV920D3CF3KNPRYN40/admin/pageAppEdit.html?spm=a1z4e7.13467989.0.0.bdcb6ddbakMjxp#/form/receipt/FORM-PFYJO8XU7G2OP1V63M57W66LM24D3H6L4AULKN6/page?navUuid=FORM-PFYJO8XU7G2OP1V63M57W66LM24D3H6L4AULKN6&_k=lxq5eu) 值多选查询表单或者流程:
- 多选条件仅仅支持一个字段;
- 以第一条查询为主数据, 合并明细;
- 数据联动可能会异常.
> 明细关联详细使用方式详见 [2.表间数据关联](https://www.yuque.com/gxg6w6/xg2yg3/txoqrm)
```
//---------------------- common ----------------------//
// 工具库: 库地址存储于 dp 在数据页面全局共享, dp 在页面加载后才有值
function loadAliworkSDK(ctx = window.LeGao.getContext()) {
const src = ctx.dp.getValue("mjs_LIB");
const script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute("src", src);
document.body.appendChild(script);
script.onload = () => _mjsOnload(ctx);
}
//---------------------- private ----------------------//
// 加载即调用方法请在此处进行调用
async function _mjsOnload(ctx) {
mjs.aliworkInitParams()
// 效验操作组权限:提交态
if (mjs._checkSubmitEnv()) { }
}
//【3.红色都是需要匹配的】明细查询和赋值
async function _queryAndUpdateCompData(ctx) {
//【3.1.多条件查询】表单查询条件:src 来源表条件组件Id,cur 当前表条件组件ID,def 默认值优先级低于来源表数据 - 多选条件仅仅支持一个字段; 以第一条查询为主数据, 合并明细; 数据联动可能会异常
const conditions = [{
src: "textField_kfnjmfq2",
cur: "multiSelectField_klu9cmvv",
def: ""
}];
const resList = await mjs._queryMultipleFormOrProcess(ctx, { formId: "FORM-JHYJNJSVPXGLZXSE43HQV9M8XE7U2UHXM00JKB3", /** 数据来源表Id */ conditions, compId_tip: "multiSelectField_klu9cmvv", /** 非必填:报错的提示到输入框,可阻断提交 */ compId_detail_src: "tableField_kfnjmfq3", /** 来源表明细组件Id */ })
//【3.2.1.多明细更新】明细组件Id映射表: src 来源明细表内组件ID,cur 当前明细表内组件ID,def 默认值优先级低于来源表数据
const props1 = [{
// 名称
src: "textField_kfnjmfq4",
cur: "selectField_kkny9ed5",
},
{
// 名称
src: "textField_kfnjmfq4",
cur: "textField_kfnjmfq4",
},
{
// 数量
src: "numberField_kfnjmfq5",
cur: "numberField_kfnjmfq5",
def: 0
},
{
// 价格
src: "numberField_kfnjmfq6",
cur: "numberField_kfnjmfq6"
},
{
// 小计
src: "numberField_kfnjmfq7",
cur: "numberField_kfnjmfq7"
}];
mjs._updateCompDetailFromDetail(ctx, { resList, props: props1, compId_detail_src: "tableField_kfnjmfq3", /** 来源表明细组件Id */ compId_detail_cur: "tableField_kfnjmfq3" /** 当前明细的compId */ }, { funcFilter: null, /** 过滤方法条件:detail => detail["textField_kfnjmfq4"] == "条件" */ })
}
//---------------------- event ----------------------//
// 【1.将事件绑定到页面】页面节点加载渲染完毕
export function didMount(ctx) {
// 加载工具库: dp & 初始化
loadAliworkSDK(ctx)
}
//---------------------- event ----------------------//
// 【2. 将事件绑定到查询条件】当查询条件发生变更
export function onMultipleConditionChange(ctx) {
// 匹配查询和赋值
_queryAndUpdateCompData(ctx)
}
```
> update 2021-02-03 w3 ➜ update 2021-03-02 w2