1. 使用背景
在流程的数据管理页中可以看到审批节点信息,却无法看到节点审批所用时长。为了查看节点审批时长,本文档使用自定义页面获取流程的审批记录,计算出流程总审批时长及各个节点审批时长,并展示在表格组件中,从而实现流程效率的监控。
在流程的数据管理页中可以看到审批节点信息,却无法看到节点审批所用时长。为了查看节点审批时长,本文档使用自定义页面获取流程的审批记录,计算出流程总审批时长及各个节点审批时长,并展示在表格组件中,从而实现流程效率的监控。
创建一个流程页面 >> 完成流程审批节点设置 >> 创建测试数据
图3.1 设置流程页面
自定义页面实现的作用是实时查看审批节点、审批人、审批时间等信息,所以需要使用两个数据源。这里分别使用了根据条件搜索流程实例 ID和获取审批记录接口。
为了实时获取流程的监控情况,会先调用搜索实例ID的接口,筛选完成对应的流程后通过获取审批记录的接口将数据审批节点的动作和审批时间等数据获取出来,在自定义页面进行展示。
图3.2.1 远程数据源配置
数据源关闭自动加载,后续为了实现分页和总计的效果,在调用数据源后使用了数据处理,返回实例 ID,总条数和当前页码。
下述代码可直接复制在获取实例 ID 接口的 didfetch 数据处理内
function didFetch(content) { //返回实例ID,总条数和当前页码 let result = { processInstanceId: content.data, totalCount: content.totalCount, currentPage: content.currentPage, } return result; // 重要,需返回 result }
由于对返回的数据做了较为复杂的格式处理,所以定义变量数据源 time 和 datas,表格组件绑定 datas 变量数据源。
图3.2.2 表格组件绑定变量数据源
图3.2.3 展示效果
因为自定义页面默认不绑定初始化函数 didmount,所以先绑定再将代码复制到 JS 面板中。
将数据处理完成后,赋值给 datas 变量数据源,并设置表格数据列字段。
绑定didmount动作
更改数据字段名称
下述代码可直接复制在 JS 面板内
export function didMount() { //用作汇总每条审批记录的数据 let value = []; //调用getInstanceIds远程数据源获取实例ID,并循环将实例ID this.dataSourceMap.getInstanceIds.load().then((response) => { console.log("response", response)//[实例ID,实例ID,...] let res = response.processInstanceId //循环将每条实例ID用作参数调用一次接口 for (let i = 0; i < res.length; i++) { const params = { processInstanceId: res[i], } //调用getOperationRecords接口获取每条流程的审批记录 this.dataSourceMap.getOperationRecords.load(params).then((content) => { //判断是HISTORY类型时,计算总审批时长,最终审批时间-提交时间 if (content[content.length - 1].type === "HISTORY") { const num_end = content[content.length - 1].operateTime; const num_start = content[0].operateTime; this.setState({ time: timeshow(num_end, num_start) }) let data = { //发起人和发起时间是content[0]里的数据,其余是获取的最后一条数据 create_name: content[0].operatorName, create_time: content[0].operateTime, actionExt: "Completed", active: content[content.length - 1].showName + "(已完成)", active_name: content[content.length - 1].operatorName + "(已完成)", active_time: content[content.length - 1].operateTime, action: content[content.length - 1].actionExt, time: state.time, onlyid: params.processInstanceId } value.push(data); } //如果不是HISTORY类型,那就是没有结束的审批流程 //需要循环获取数据列的数据,将当前的审批节点数据push在数据列中 for (let j = 0; j < content.length; j++) { console.log("content[j]", content[j]) if (content[j].type === "TODO") { let data = { //发起人和发起时间是content[0]里的数据,其余变成TODO类型时的数据也就是进行中的那条数据 create_name: content[0].operatorName, create_time: content[0].operateTime, actionExt: content[j].actionExt, active: content[j].showName + "(当前)", active_name: content[j].operatorName + "(当前)", active_time: content[j].activeTime, onlyid: params.processInstanceId, } value.push(data); } } //携带总计条数和数据列的值赋值给datas变量数据源 let result = { data: value, totalCount: response.totalCount, } this.setState({ datas: result }) }) } }) } //timeshow方法,传入结束时间和开始时间计算时间差 export function timeshow(num_end,num_start){ const num = (num_end - num_start) / 1000; const day = Math.floor(num / 86400) + "d "; const hour = Math.floor((num % 86400) / 3600) + "h "; const min = Math.floor(((num % 86400) % 3600) / 60) + "min "; const s = Math.floor(((num % 86400) % 3600) % 60) + "s "; if (day == "0d ") { //判断当天数等于0d时 if (hour == "0h ") { //在当天数等于0d时,继续判断当小时等于0h时 if (min == "0min ") { //在当天数等于0d时,当小时等于0h时,继续判断分等于0时 //返回秒 return `${s}` } else { //min不等于0时,则将分钟和秒返回 return `${min}${s}` } } else { //天数等于0d,小时不等于0h时,返回小时分钟 return `${hour}${min}${s}` } } else { //当天数不等于0d时,将天数小时分钟拼接一起返回 return `${day}${hour}${min}${s}` } }
图3.2.4 效果演示
通过表格的树形结构展示各个节点的审批时长。
在表格中开启「树形表格」,注意:开启「树形表格」后,数据中需要包含 children 字段才会显示树形表格。
下述代码可直接复制在 JS 面板内,12~56 行代码配置 children 内数据
export function didMount() { let value = []; this.dataSourceMap.getInstanceIds.load().then((response) => { console.log("response", response) let res = response.processInstanceId for (let i = 0; i < res.length; i++) { const params = { processInstanceId: res[i], } this.dataSourceMap.getOperationRecords.load(params).then((content) => { //存储树形表格内的children格式,详情表格格式可以点击查看表格数据源属性中的编辑代码或参考文档:https://www.aliwork.com/developer/table-pc#tree const children = []; //循环一个流程中的所有节点的审批记录,最终将判断好的数据格式push到children中 for (let item = 0; item <content.length; item++) { //判断当审批状态是next或submit或doing时没有本节点的审批时间,所以不计算本节点到上一个节点所用审批时长 if (content[item].actionExt === "next" || content[item].actionExt === "submit" || content[item].actionExt === "doing" ){ //重新提交的审批状态是submit,这里对它作判断计算了审批时长 if(content[item].showName === "重新提交" ){ this.setState({ time: timeshow(content[item].operateTime, content[item-1].operateTime) }) let children_data = { actionExt: content[item].actionExt, active: content[item].showName, active_name: content[item].operatorName, active_time: content[item].operateTime, time: state.time, onlyid: params.processInstanceId } children.push(children_data); }else{ //没有本节点审批时间的不加time let children_data = { actionExt: content[item].actionExt, active: content[item].showName, active_name: content[item].operatorName, active_time: content[item].operateTime, onlyid: params.processInstanceId } children.push(children_data); } }else{ this.setState({ time: timeshow(content[item].operateTime, content[item-1].operateTime) }) let children_data = { actionExt: content[item].actionExt, active: content[item].showName, active_name: content[item].operatorName, active_time: content[item].operateTime, time: state.time, onlyid: params.processInstanceId } children.push(children_data); } } //判断是HISTORY类型时,计算总审批时长,最终审批时间-提交时间 if (content[content.length - 1].type === "HISTORY") { const num_end = content[content.length - 1].operateTime; const num_start = content[0].operateTime; this.setState({ time: timeshow(num_end, num_start) }) let data = { //发起人和发起时间是content[0]里的数据,其余是获取的最后一条数据 create_name: content[0].operatorName, create_time: content[0].operateTime, actionExt: "Completed", active: content[content.length - 1].showName + "(已完成)", active_name: content[content.length - 1].operatorName + "(已完成)", active_time: content[content.length - 1].operateTime, action: content[content.length - 1].actionExt, time: state.time, children: children, onlyid: params.processInstanceId } value.push(data); } //循环获取数据列的数据,当前的审批节点数据push在数据列中 for (let j = 0; j < content.length; j++) { console.log("content[j]", content[j]) if (content[j].type === "TODO") { let data = { //发起人和发起时间是content[0]里的数据,其余变成TODO类型时的数据也就是进行中的那条数据 create_name: content[0].operatorName, create_time: content[0].operateTime, actionExt: content[j].actionExt, active: content[j].showName + "(当前)", active_name: content[j].operatorName + "(当前)", active_time: content[j].activeTime, children: children, onlyid: params.processInstanceId, } value.push(data); } } //携带总计条数和数据列的值赋值给datas变量数据源 let result = { data: value, totalCount: response.totalCount, } this.setState({ datas: result }) }) } }) } //timeshow方法,传入结束时间和开始时间计算时间差 export function timeshow(num_end,num_start){ const num = (num_end - num_start) / 1000; const day = Math.floor(num / 86400) + "d "; const hour = Math.floor((num % 86400) / 3600) + "h "; const min = Math.floor(((num % 86400) % 3600) / 60) + "min "; const s = Math.floor(((num % 86400) % 3600) % 60) + "s "; if (day == "0d ") { //判断当天数等于0d时 if (hour == "0h ") { //在当天数等于0d时,继续判断当小时等于0h时 if (min == "0min ") { //在当天数等于0d时,当小时等于0h时,继续判断分等于0时 //返回秒 return `${s}` } else { //min不等于0时,则将分钟和秒返回 return `${min}${s}` } } else { //天数等于0d,小时不等于0h时,返回小时分钟 return `${hour}${min}${s}` } } else { //当天数不等于0d时,将天数小时分钟拼接一起返回 return `${day}${hour}${min}${s}` } }
图3.2.5 效果演示
表格组件绑定动作
更改分页设置
前置条件:获取流程实例 ID 的远程数据源内做 didfetch 数据处理,已在 JS 面板内定义 timeshow 方法。
下述代码可直接复制在JS面板内,注意:替换流程的 formUuid
export function onFetchData(params) { //清空datas变量数据源,不清空有可能会造成翻页时数据展示错误 this.setState({ datas:[] }) // 如果是搜索的话翻页重置到 1 if (params.from === 'search') { params.currentPage = 1; } //定义newvalue数组,存放表格数据 const newvalue = []; //配置获取实例id数据源参数,获取翻页后的第params.currentPage页数据 const param_1 = { formUuid: "FORM-BB866481KHMT8HUAWKX4MINJEO0Z1ICW0XKTKW3", pageSize: 5, currentPage: params.currentPage, } this.dataSourceMap.getInstanceIds.load(param_1).then((response) => { let res = response.processInstanceId for (let i = 0; i < res.length; i++) { const params = { processInstanceId: res[i], } this.dataSourceMap.getOperationRecords.load(params).then((content) => { const children = []; for (let item = 0; item < content.length; item++) { if (content[item].actionExt === "next" || content[item].actionExt === "submit" || content[item].actionExt === "doing") { if (content[item].showName === "重新提交") { this.setState({ time: timeshow(content[item].operateTime, content[item - 1].operateTime) }) let children_data = { actionExt: content[item].actionExt, active: content[item].showName, active_name: content[item].operatorName, active_time: content[item].operateTime, time: state.time, onlyid: params.processInstanceId } children.push(children_data); } else { let children_data = { actionExt: content[item].actionExt, active: content[item].showName, active_name: content[item].operatorName, active_time: content[item].operateTime, onlyid: params.processInstanceId } children.push(children_data); } } else { this.setState({ time: timeshow(content[item].operateTime, content[item - 1].operateTime) }) let children_data = { actionExt: content[item].actionExt, active: content[item].showName, active_name: content[item].operatorName, active_time: content[item].operateTime, time: state.time, onlyid: params.processInstanceId } children.push(children_data); } } for (let j = 0; j < content.length; j++) { if (content[j].type === "TODO") { let data = { create_name: content[0].operatorName, create_time: content[0].operateTime, actionExt: content[j].actionExt, active: content[j].showName + "(当前)", active_name: content[j].operatorName + "(当前)", active_time: content[j].activeTime, children: children, onlyid: params.processInstanceId, } newvalue.push(data); } } if (content[content.length - 1].type === "HISTORY") { const num_end = content[content.length - 1].operateTime; const num_start = content[0].operateTime; this.setState({ time: timeshow(num_end, num_start) }) let data = { create_name: content[0].operatorName, create_time: content[0].operateTime, actionExt: "Completed", active: content[content.length - 1].showName + "(已完成)", active_name: content[content.length - 1].operatorName + "(已完成)", active_time: content[content.length - 1].operateTime, action: content[content.length - 1].actionExt, time: state.time, children: children, onlyid: params.processInstanceId } newvalue.push(data); } //currentPage控制页码选中,totalCount控制总计条数,data是控制数据 let result = { data: newvalue, totalCount: response.totalCount, currentPage:response.currentPage } this.setState({ datas: result }) }) } }) }
设置表格的操作列 >> 添加名为详情的操作项 >> 点击编辑 >> 绑定回调动作
图3.2.6 绑定回调函数
下述代码可直接复制编辑 JS 面板内,注意:替换链接为目标流程详情页链接。
export function onActionClick(rowData) { console.log(rowData); this.utils.router.push("https://www.aliwork.com/APP_JKH50FL33A0H8GJBQEHL/processDetail?formInstId=" + rowData.onlyid, {}, true, true); }
图4-1 实现效果
在线体验请移步开发者中心 ? 自定义页面查看效率