业务场景一:审批表单页面自定义
在学校这种特殊场景下,微搭官方提供的审批页面模板样式不满足要求,需要审批的表单页面显示成套红文件样式,且将流程中的信息展示在表单业务中,如下图所示:
开发实践
以上场景需要调用流程 OpenAPI 实现的功能点:
步骤1:页面中不使用官方预置的标准审批组件,自定义操作按钮绑定触发流程操作
按钮绑定自定义方法,实现审批操作代码示例如:
/**** 可通过 $page 获取或修改当前页面的 变量 状态 handler lifecycle 等信息* 可通过 app 获取或修改全局应用的 变量 状态 等信息* 具体可以console.info 在编辑器Console面板查看更多信息* 注意:该方法仅在所属的页面有效* 如果需要 async-await,请修改成 export default async function() {}**//*** @param {Object} event - 事件对象.* @param {string} event.name - 事件名.* @param {string} event.target - 事件的目标节点(触发该事件的节点).* @param {string} event.currentTarget - 当前处理该事件的节点。包括冒泡和捕获事件。.** @param {Object} data* @param {any} data.target - 获取事件传参的数据**/export default async function ({ event, data }) {const userInfo = await $app.auth.getUserInfo();console.log('$app.auth.getUserInfo:', userInfo);let zwname = userInfo.nickName;let currentDate = $app.utils.formatDate(Date.now(), "yyyy年mm月dd日 HH时MM分ss秒");let comment = '';if ($page.dataset.state.isBMLD) {if (!$page.dataset.state.bmld) {app.showToast({title: '审核意见不能为空!',icon: 'none',duration: 2000, // 2秒mask: false,});return}comment = $page.dataset.state.bmld + ' ' + zwname + ' ' + currentDate;}if ($page.dataset.state.isFGLD) {if (!$page.dataset.state.ldyj) {app.showToast({title: '审核意见不能为空!',icon: 'none',duration: 2000, // 2秒mask: false,});return}comment = $page.dataset.state.ldyj + ' ' + zwname + ' ' + currentDate;}$page.dataset.state.saveloading = true;$app.showLoading({title: "数据提交中"});let Approver = {"Type": 4,"UserId": $page.dataset.state.userid}// 调用连接器 调用流程OpenAPI CompleteTasklet postparams = { "TaskId": $page.dataset.state.taskid, "ButtonAction": 2, "Comment": comment, "Approver": Approver };console.log(postparams, '查询入参');const result = await app.cloud.callConnector({name: 'cxmxhgzlztsj_XXXXX', //连接器标识methodName: 'method_XXXXX', // 连接器方法标识params: postparams, // 方法入参})setTimeout(function () {app.showToast({title: '提交成功',icon: 'success',duration: 1000, // 2秒});$app.hideLoading();$page.dataset.state.saveloading = false;app.redirectTo({pageId: 'frzs_93k87uf_list', // 页面 Id});}, 5000);//5秒后返回列表}
步骤2:各个节点审批意见均要显示在业务表中
数据模型中需要增加字段用于记录流程实例 ID、任务 ID、审批意见等,在审批操作后调用自定义 APIs 写入数据源,以请假审批为例,流程配置示意如下:
开始节点后的更新节点配置如下:
审批节点配置页面输入变量和页面输出变量。
自定义 API 节点配置如下:
描述:
qjsq/流程实例 ID(文本):是开始节点-输入变量-实例 ID。
outqjsq/流程任务 ID(文本):审批节点-输入变量-任务 ID。
qjsq_hr4ya6t:数据源标识。
fgldspyj:需要更新的审批意见字段。
qjsq/数据标识(文本):开始节点-输入变量-数据记录 ID。
结束节点前的更新节点配置如下:
审批页面代码编辑器示例代码:
1. 需要审批页面加载数据模型的最新记录可参考以下代码:
// flowLoad 审批页面改为渲染数据模型记录export default async function ({ event, data }) {const { initialValues, formType } = event?.detail || {};+ /** 根据具体业务情况填入dataSourceName 和 dataId */+ initialValues.Id+ const requestDataSourceItemParams = { dataSourceName: '', dataId: initialValues.Id };+ /** 获取数据源数据 */+ const dataSourceItem = await getDataSourceItem(requestDataSourceItemParams) || {};for (let id in $page.widgets) {const widget = $page.widgets[id];if (widget?.initialValues && widget?.dataSourceName) {+ /**【修改】 dataSourceItem 替换原有的 initialValues */- widget.setValue(initialValues?.[widget?.dataSourceName]);+ widget.setValue(dataSourceItem?.[widget?.dataSourceName]);widget.formType = formType;}}}/*** 获取数据源记录* @param meta 请求数据源参数.* @param meta.dataSourceName 数据源标识* @param meta.id 数据Id* @returns {* [dataSourceName]: wedaDataItem* }* @link https://docs.cloudbase.net/lowcode/datasource/usage?from_wecom=1#%E5%9C%A8%E4%BD%8E%E4%BB%A3%E7%A0%81%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%95%B0%E6%8D%AE%E6%BA%90*/async function getDataSourceItem(meta) {const { dataSourceName, id } = params;try {const data = await app.cloud.callDataSource({dataSourceName,methodName: 'wedaGetItem',params: { _id: id }});return {[dataSourceName]: data || {}}} catch (error) {console.error(error)return null}}
2. 提交时将当前任务 ID 写回流程。
/**** 可通过 $page 获取或修改当前页面的 变量 状态 handler lifecycle 等信息* 可通过 app 获取或修改全局应用的 变量 状态 等信息* 具体可以console.info 在编辑器Console面板查看更多信息* 注意:该方法仅在所属的页面有效* 如果需要 async-await,请修改成 export default async function() {}**//*** @param {Object} event - 事件对象.* @param {string} event.name - 事件名.* @param {string} event.target - 事件的目标节点(触发该事件的节点).* @param {string} event.currentTarget - 当前处理该事件的节点。包括冒泡和捕获事件。.** @param {Object} data* @param {any} data.target - 获取事件传参的数据**/export default async function ({ event, data }) {const formDataMap = {};for (let id in $page.widgets) {const widget = $page.widgets[id];if (widget?.formItems && widget?.submit) {const dataSourceName = widget.dataSourceName;const { flowTaskId } = getCurrentUrlParams();const values = await widget?.submit();/** flowTaskId 放入map中 */formDataMap[dataSourceName]=values;/** 这里的 taskId 可以按照实际业务数据需要自行定义 */formDataMap[dataSourceName] = { ...values, "lcrwid": flowTaskId }}}event?.detail?.onGetFormData?.(formDataMap);}const getCurrentUrlParams = () => {const currentSearch = location.hash?.split('?')?.[1];function parse(url) {const obj = {};url.replace(/([^?&=]+)=([^&]+)/g, (_, k, v) => (obj[k] = v));return obj;}if (currentSearch) {return parse(currentSearch);}return {}};
3. 自定义 API 通过 HTTP 请求调用开发者封装的接口,后端实现更新审批意见到数据模型中,实现伪代码如下:
// 步骤1:调用流程 OpenAPI 查询刚操作过的审批任务 接口7 - GetFlowInstanceTasks 入参TaskIdList<FlowInstanceTaskData> = query GetFlowInstanceTasks(TaskId)FlowTask = List<FlowInstanceTaskData>.get(0)// 步骤2:调用流程 OpenAPI 查询操作记录 接口8 - GetFlowInstanceOperations 入参 FlowTask.InstanceId, FlowTask.ElementIdlist<FlowOperation> = query GetFlowInstanceOperations(FlowTask.InstanceId, FlowTask.ElementId)for (FlowOperation : list<FlowOperation>) {// 解析json string 获取审批意见comment += json.parse(FlowOperation.Content.operationComment)}// 步骤3:调用数据源 OpenAPI 更新数据记录入参数据源记录 ID、审批意见 comment
注意
目前审批意见更新到数据模型中等类似的功能,暂时需要 开发者或 ISV 有服务器后端开发实现,公有云通过自定义代码调用需要鉴权的 HTTP 请求时还在支持中,私有云不支持云函数执行(自定义代码执行),也需要后端实现。
步骤3:完成审批后跳转到指定页面
1. 要完成升级组件库。
2. 在审批页面中,选中流程操作组件,查看当前组件是否带升级提示,如果有,需要单击升级到新版。
3. 升级完成后,再次选中流程操作组件,单击其他触发事件。
4. 在添加事件对话框中会出现各种完成后事件,选择指定场景,例如完成审批后,再选择执行动作,选择页面跳转,按需配置。
步骤4:在流程布局中使用流程数据搭建页面:
有的时候可能会遇到这种场景,需要在这个页面中自由展示流程变量的值,而且不一定需要流程组件,这个时候可以使用流程布局这个组件。
1. 先生成一个任意的审批页面,然后在左侧大纲树中,找到流程布局组件,将流程详情和流程操作面板这两个插槽内的内容全部删掉,如果有个别组件需要用的话,可以保留一些组件。
2. 在流程详情这个插槽中可以添加一些组件。
3. 给组件绑定流程数据,可以通过建立一个变量,来作为一个媒介。
4. 先建立一个变量,变量类型为 Object,数据模型为 JSON。作用域建议选择当前页面。
5. 然后给这个变量赋值,打开代码编辑器,找到当前页面的 handler, 找到 flowLoad。获取到 initialValues 之后,将 initialValues 赋给刚刚建立的变量。
initialValues 为流程中流转的变量:
const { initialValues = test, formType } = event?.detail || {};+ $page.dataset.state.myFlowData = initialValues;
initialValues 即流程中的变量数据,格式为:
type InitialValuesType = {[dataSourceName: string] : {[key: string]: any}}
initialValues 示例数据:
{"xxxx_j95g7nf": {"name": "Peter","age": 12,},"xxxx_hr4ya6t": {"address": "No.1 Street"}}
6. 在组件中绑定数据,即可完成对流程数据的使用。
用于自定义流程数据展示页面。
步骤5:利用流程输出的对象列表数据生成一个列表
1. 流程触发时需要传入对象列表,可在开始节点配置对象列表,并在页面添加触发流程事件,将变量(类型为 Array)绑定到对象列表参数上。
2. 页面渲染对象列表时,先在人工节点中配置对象列表。
3. 首先升级到最新版本组件库。
并将每个审批组件均进行新旧版本切换,切换成最新的,包括基本信息、流程记录、流程操作组件、流程图示组件、流程布局。
4. 建立变量,在当前页面建立一个变量,变量类型为 Array,数据模型为 JSON。作用域建议选择当前页面。
5. 然后给这个变量赋值,打开代码编辑器,找到当前页面的 handler,找到 flowLoad。然后进行以下变更。
- const { initialValues, formType } = event?.detail || {};+ const { initialValues, formType, objectListValues } = event?.detail || {};+ $page.dataset.state.objectList = objectListValues?.kongtest_92tbnf0 || [];
objectListValues 为解构出来的对象列表值主要格式如下,即数据源标识和数组的键值对。
type ObjectListValuesType = {[dataSourceName: string] : Array<Record<string,any>>}
示例数据:
{"xxxx_j95g7nf": [{"name": "Peter","age": 12,},{"name": "Tom","age": 24,},],"xxxx_hr4ya6t": [{"address": "No.1 Street"},{"address": "No.2 Street"}]}
根据这样的数据格式,可以给变量赋值,objectListValues 后面的属性按照实际的数据源标识进行获取。
$page.dataset.state.objectList = objectListValues?.xxxx_j95g7nf || [];
6. 用这个变量进行页面渲染。首先准备一个列表组件,这个组件可以自己定义。示例中使用一个容器,包裹一些文本字段。核心思路是:对一个容器设置条件展示,然后对容器内部的展示字段绑定循环对象中的值。首先,给容器设置循环展示,绑定表达式,在表达式输入框中输入之前建立的变量。
然后选中容器内的文本,给文本绑定一个值。在绑定弹框中选中循环对象,然后就出现了刚刚绑定循环变量的组件 ID,然后再点这个 ID,我们就可以在表达式输入框中获得一个表达式,这个表达式其实就是要循环的对象,也就是对象列表中的每一个对象。
然后可以基于这个表达式继续写属性。这个属性就来源于对象列表中每一个对象中包含的属性如下:
$for.id173.name
如果对象中又包含对象还可以继续往下去访问它的属性,例如:
$for.id173.info.tel
绑定完之后,发布应用,就可以展示列表界面了。
注意
如果这样操作的话,需要优先设计好这个容器的样式。因为一旦设置条件展示的绑定变量之后,编辑器中预览的内容会不可见。这个时候想要再次编辑其样式的话,需要移除容器的循环绑定以及容器内的内容绑定,才会再次可见这个容器。
业务场景二:业务列表展示流程状态数据
学校已有统一的官方门户,希望通过该门户的应用访问入口,直接打开应用下业务列表页面,业务方希望打开的业务列表页面中不仅显示业务字段信息还包括该业务关联的流程相关的数据信息。
微搭官方提供的流程中心目前只包括了流程实例的数据,未包括业务相关信息,且官方预置样式不满足要求。包含流程数据的业务列表效果如下图所示:
开发实践
步骤1:列表中包括业务数据、及流程实例数据
在数据模型中增加字段流程实例 ID,用于记录数据模型事件触发产生的流程实例 ID。
在流程开始节点后添加更新记录节点,设置赋值为流程实例 ID。
步骤2:根据流程实例的状态来回显业务的流程状态
在数据模型中增加字段用于记录流程状态、审批任务跳转链接、流程处理人等信息。
应用代码编辑器中,lifecycle 处理逻辑示意如下:
/** 可通过 $page 获取或修改当前页面的 变量 状态 handler lifecycle 等信息* 可通过 app 获取或修改全局应用的 变量 状态 等信息* 具体可以 console.info 在编辑器Console面板查看更多信息* 如果需要 async-await,请在方法前 async*/export default {async onPageLoad(query) {console.log('---------> LifeCycle onPageLoad', query)// forDateMethod()$app.showLoading({title: "加载中",});$page.dataset.state.isShow = false;// async function forDateMethod() {//console.log('---------> LifeCycle onPageLoad', query)// 获取当前用户信息const userInfo = await $app.auth.getUserInfo();//console.log('---------> LifeCycle onPageLoad', query)// 根据业务需要查询数据源记录列表 如 任务处理人(包含当前登录用户)、流程状态const wherelist = [{ key: "lcrwclr", rel: "search", val: userInfo.userId }, { key: "lczt", rel: "neq", val: "3" }];let res = await app.cloud.callDataSource({dataSourceName: 'frzs_XXXXX',methodName: 'wedaGetRecords',params: { "pageSize": 10000, "where": wherelist, "orderBy": "createdAt", "orderType": "desc" }, // 方法入参})let modeldata = res.records;// modeldata.map(async (jitem) => {// if(jitem.lcslid) {await Promise.all(modeldata.map(async (item) => {if (item.lcslid) {// 调用连接器 GetFlowInstanceTasks 流程OpenAPI 根据流程实例Id查询审批任务列表let postparams = { "InstanceId": item.lcslid };let result = await app.cloud.callConnector({name: 'cxlcslxq_XXXXX', //连接器标识methodName: 'method_XXXX', // 连接器方法标识params: postparams, // 方法入参})if (JSON.stringify(result.Response.Data) == "{}") {return {}} else {if (result.Response.Data.Result.length == 0) {return {}} else {let currentdata = result.Response.Data.Result;//得到所有流程审批人 + 发起人let cluserlist = currentdata.map(item => item.TaskAssignee).map(item => item.UserId).join(',');let createuser = currentdata.map(item => item.FlowStartUser).map(item => item.UserId)[0];let newuserlist = cluserlist + ',' + createuser;// 获取当前登录用户的审批任务let currentapprover = result.Response.Data.Result.filter(item => { if (item.TaskAssignee.UserId == userInfo.userId) { return item } });console.log(currentapprover, 'currentapprover');if (currentapprover.length > 0) {if (currentapprover.length > 1) {// 当前登录用户下有多个审批任务,找到最新的任务let backdata = currentapprover;var sorted = backdata.slice().sort(function (a, b) {return new Date(a.StartTime) - new Date(b.StartTime);});let newtaskid = sorted.pop();await updataModel(newuserlist, item._id, newtaskid)return newtaskid} else {// 当前登录用户下只有一个审批任务 直接返回await updataModel(newuserlist, item._id, currentapprover[0])return currentapprover[0]}} else {// 当前登录用户下没有审批任务,但数据源记录处理人(发起人+审批人)中包含,即为发起// 通用选项集中业务自定义流程状态 1:待办 2:已办 3:已完结 4:发起let backdata = result.Response.Data.Result;var sorted = backdata.slice().sort(function (a, b) {return new Date(a.StartTime) - new Date(b.StartTime);});let newtaskid = sorted.pop();await updataModel(newuserlist, item._id, newtaskid, '4')return newtaskid}}}}}))$page.dataset.state.isShow = true;$app.hideLoading();//更新模型方法 将流程审批任务状态、跳转链接、处理人列表更新到数据源中async function updataModel(newuserlist, id, newtaskid, fq) {var updateparams = {_id: id,//基本信息---lczt: fq ? fq : (newtaskid.TaskStatus == 3 ? 2 : newtaskid.TaskStatus) + '',lcrwclr: newuserlist,lcurl: newtaskid.PageTransitionUrl}const resultupdate = await app.cloud.callDataSource({dataSourceName: 'frzs_XXXXX', //连接器标识methodName: 'wedaUpdate', // 连接器方法标识params: updateparams, // 方法入参})console.log(resultupdate, '更新成功后的值')}},onPageShow() {console.log('---------> LifeCycle onPageShow')},onPageReady() {console.log('---------> LifeCycle onPageReady')},onPageHide() {console.log('---------> LifeCycle onPageHide')},onPageUnload() {console.log('---------> LifeCycle onPageUnload')},}
业务场景三:流程督查督办中的流程数据统计
一般学校或者企业需要数据仓库离线计算流程在流转过程中,各个环节执行的效率情况,需要统计分析流程各个环节执行的时间。
开发实践
注意
目前督查督办实现暂时需要开发者或 ISV 有服务器后端开发实现。
步骤1:以流程维度统计
统计每个流程发生的实例数、不同状态的流程数据、以及流程平均完成时间。
数据模型中增加字段流程标识 processKey,用于建立流程和数据模型记录的关系。
实现伪代码:// 步骤1:调用数据源 OpenAPI 查询记录Map<String, String> processKey2RecordId // 构造记录流程标识和数据模型记录id的映射关系//步骤2:获取到已经部署的流程列表 - 调用接口2 ListDeployedFlows 出参 list<DeployedProcessInfo>for(DeployedProcessInfo processInfo : list<DeployedProcessInfo>) {// 步骤3:获取该流程的实例列表 - 调用接口6 GetFlowInstances, 入参processInfo.ProcessKeylist<FlowInstanceData> = query GetFlowInstances(processInfo.ProcessKey)3. //发起数int fqs = list<FlowInstanceData>.size();// 总办结时长long sumtime=0;4. //处理中数int clz =0 ;5. //已办结数int ybj=0;for(FlowInstanceData flowInstanceData:list<FlowInstanceData>){if (flowInstanceData.endtime == null) {clz ++ ;}if (flowInstanceData.endtime != null) {ybj ++ ;sumtime + = flowInstanceData.endtime -flowInstanceData.starttime}}6.// 办结率办结率 = 已办结数/发起数7. // 平均办理时长平均办理时长 = sumtime / 已办结数8. processKey2RecordId 查询数据源记录id// 调用数据源OpenAPI 更新记录}
步骤2:以用户维度统计
统计不同老师参与审批的次数、处理完成率等。
数据模型中增加字段用户 id userId,用于建立用户 ID 和数据模型记录的关系。
实现伪代码:// 步骤1:调用数据源 OpenAPI 查询记录Map<String, String> userId2RecordId // 构造记录用户Id和数据源记录id的映射关系//步骤2:分页查询用户列表,调用用户权限接口list<User> = describeUserByOrgId()for (User user : list<User>) {//步骤3:查询流程任务信息,调用接口7 GetFlowInstanceTasks 入参TaskAssignee UserId=user.id Type=4list<FlowInstanceTaskData> = GetFlowInstanceTasks(TaskAssignee)4.// 已处理数for(FlowInstanceTaskData : list<FlowInstanceTaskData>){if(FlowInstanceTaskData.taskStatus==2){已处理数 ++sumtime += FlowInstanceTaskData.endtime - FlowInstanceTaskData.starttime}if(FlowInstanceTaskData.taskStatus==1){待办数 ++}}5.// 参与审批次数 = 已处理数 + 待办数6.// 处理完成率处理完成率 = 已处理数/参与审批次数7.// 平均等待时长平均等待时长 = sumtime/已处理数8. userId2RecordId 查询数据源记录id// 调用数据源OpenAPI 更新记录}
步骤3:以流程实例维度统计
统计某一个流程下所有运行实例的状态、当前处理节点、审批人等。
数据模型中增加字段 实例 id instanceId,用于建立流程实例和数据模型记录的关系。
实现伪代码:// 步骤1:调用数据源 OpenAPI 查询记录Map<String, String> instanceId2RecordId // 构造记录流程实例 ID 和数据源记录 ID 的映射关系//步骤2:查询流程实例列表,调用接口6 GetFlowInstanceslist<FlowInstanceData> = GetFlowInstances(ProcessKey)for(FlowInstanceData : list<FlowInstanceData>){// 2. 标题 ,基于实例 ID 查数据源 FlowInstanceData.instanceId// 3. 发起人 = FlowInstanceData.StartUsername// 4. 当前节点// 5. 当前节点审批人if (FlowInstanceData.endtime!=null) {当前节点 = 已办结} else {//步骤3:查询流程任务信息 - 接口7 GetFlowInstanceTaskslist<FlowInstanceTaskData> = GetFlowInstanceTasks(FlowInstanceData.instanceId)for (FlowInstanceTaskData : list<FlowInstanceTaskData>) {if (FlowInstanceTaskData.TaskStatus=1) {当前节点 = FlowInstanceTaskData.elementName等待时间 = now - FlowInstanceTaskData.starttime当前节点id curElementId = FlowInstanceTaskData.elementIdbreak;}}for (FlowInstanceTaskData : list<FlowInstanceTaskData>) {if (FlowInstanceTaskData.ElementId == curElementId) {当前节点审批人 curApproverIds.add(FlowInstanceTaskData.TaskAssignee.UserId)当前节点审批人状态 = FlowInstanceTaskData.EndTime == null ? "待审批" : "同意"}}}//步骤4:调用户权限接口查当前审批人部门和名称,接口2 describeWedaUser,参数 UserIdfor (UserId : curApproverIds) {User user = query describeWedaUser(UserId)6. 当前节点审批人.add(user.name)7. 所在部门.add(user.mainOrg)}8. instanceId2RecordId 查询数据源记录 ID// 调用数据源OpenAPI 更新记录}