Custom page viewing process efficiency
1. Use background
On the data management page of the process, you can see the approval node information, but you cannot see the duration of the node approval. To view the node approval duration, this document uses the custom page to obtain the approval records of the process, calculates the total approval duration of the process and the approval duration of each node, and displays them in the table component, so as to monitor the process efficiency.
2. Video tutorial
3. Procedure
3.1 Step 1: create a process page
Create a process page>Complete process approval node settings>Create test data
Figure 3.1 setup process page
3.2 Step 2: Create a custom View page
The custom page allows you to view information such as the approval node, approver, and approval time in real time. Therefore, you need to use two data sources. Used here separatelySearch for process instance IDs based on criteriaAndObtain approval RecordsInterface.
3.2.1 define remote data sources
To obtain the monitoring status of the process in real time, the API to search for the instance ID is called first, after filtering the corresponding process, obtain the data such as the action and approval time of the data approval node through the interface of obtaining approval records, and display it on the custom page.
Figure 3.2.1 remote data source configuration
The data source is disabled for automatic loading. In order to achieve paging and total effects, data processing is used after the data source is called, and the instance ID, total number, and current page number are returned.
The following code can be directly copied to the didfetch data processing of the API for obtaining the instance ID.
function didFetch(content) {
//返回实例ID,总条数和当前页码
let result = {
processInstanceId: content.data,
totalCount: content.totalCount,
currentPage: content.currentPage,
}
return result; // 重要,需返回 result
}
3.2.2 define variable data sources
Because the returned data is processed in a complex format, variable data sources time and datas are defined, and table components bind datas variable data sources.
Figure 3.2.2 binding variable data sources to table components
3.2.3 JS panel calls remote data sources and processes data
Figure 3.2.3 display effect
Because the initialization function didmount is not bound to the custom page by default, the code is bound first and then copied to the JS panel.
After the data is processed, assign the value to the datas variable data source and set the table data column fields.
Bind didmount actions
Change the data field name
The following code can be copied directly into the JS panel.
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 show the approval duration of each node
Figure 3.2.4 Effect demonstration
The tree structure of the table shows the approval duration of each node.
Open tree table in the table. Note: After the tree table is enabled, children fields are required in the data to display the tree table.
The following code can be directly copied in the JS panel, 12 to 56 lines of code configurationData within 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://developers.aliwork.com/docs/components/advanced/table
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 implement pagination data display
Figure 3.2.5 effect demonstration
Table component binding action
Change paging settings
Precondition: the remote data source that obtains the ID of the process instance performs didfetch data processing. timeshow methods have been defined in the JS panel.
The following code can be directly copied in the JS panel,Note: replace the formUuid of the process
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 set details to jump
Set the action column of the table>Add an action item named details>Click edit>Bind callback actions
Figure 3.2.6 bind callback functions
The following code can be directly copied and edited in the JS panel,Note: replace the link with the link on the target process details page.
export function onActionClick(rowData) {
console.log(rowData);
this.utils.router.push("https://www.aliwork.com/APP_JKH50FL33A0H8GJBQEHL/processDetail?formInstId=" + rowData.onlyid, {}, true, true);
}
4. Achieve results
Figure 4-1 implementation effect
5. Try it online
For online experience, please go to the Developer Center.👉Custom page viewing efficiency
-------------------- Get the latest information YIDA, welcome to follow US--------------------