From a383363e69a35c9dea942e7b9893b8ad32e4cdbe Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 2 Mar 2025 22:30:10 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat=EF=BC=9A=20=E6=96=B0=E5=A2=9E=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E7=9A=84?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/bpm/processInstance/index.ts | 5 +++ .../SimpleProcessDesignerV2/src/consts.ts | 5 +++ .../detail/ProcessInstanceOperationButton.vue | 38 ++++++++++--------- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/api/bpm/processInstance/index.ts b/src/api/bpm/processInstance/index.ts index 5d6eefd7..06d0e1b9 100644 --- a/src/api/bpm/processInstance/index.ts +++ b/src/api/bpm/processInstance/index.ts @@ -102,3 +102,8 @@ export const getFormFieldsPermission = async (params: any) => { export const getProcessInstanceBpmnModelView = async (id: string) => { return await request.get({ url: '/bpm/process-instance/get-bpmn-model-view?id=' + id }) } + +// 获取下一个执行的流程节点 +export const getNextFlowNodes = async (params: any) => { + return await request.get({ url: '/bpm/process-instance/get-next-flow-nodes', params }) +} \ No newline at end of file diff --git a/src/components/SimpleProcessDesignerV2/src/consts.ts b/src/components/SimpleProcessDesignerV2/src/consts.ts index c5404f18..97a6b024 100644 --- a/src/components/SimpleProcessDesignerV2/src/consts.ts +++ b/src/components/SimpleProcessDesignerV2/src/consts.ts @@ -162,6 +162,10 @@ export enum CandidateStrategy { * 指定用户 */ USER = 30, + /** + * 审批人自选 + */ + APPROVE_USER_SELECT = 34, /** * 发起人自选 */ @@ -542,6 +546,7 @@ export const CANDIDATE_STRATEGY: DictDataVO[] = [ { label: '连续多级部门负责人', value: CandidateStrategy.MULTI_LEVEL_DEPT_LEADER }, { label: '指定岗位', value: CandidateStrategy.MULTI_LEVEL_DEPT_LEADER }, { label: '发起人自选', value: CandidateStrategy.START_USER_SELECT }, + { label: '审批人自选', value: CandidateStrategy.APPROVE_USER_SELECT }, { label: '发起人本人', value: CandidateStrategy.START_USER }, { label: '发起人部门负责人', value: CandidateStrategy.START_USER_DEPT_LEADER }, { label: '发起人连续部门负责人', value: CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER }, diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue index 9e6dbc69..1c0e8919 100644 --- a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue +++ b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue @@ -718,29 +718,31 @@ const closePopover = (type: string, formRef: FormInstance | undefined) => { const initNextAssigneesFormField = async () => { // 获取修改的流程变量, 暂时只支持流程表单 const variables = getUpdatedProcessInstanceVariables() - const data = await ProcessInstanceApi.getApprovalDetail({ + const data = await ProcessInstanceApi.getNextFlowNodes({ processInstanceId: props.processInstance.id, + taskId: runningTask.value.id, processVariablesStr: JSON.stringify(variables) }) - - const activityId = data.todoTask?.taskDefinitionKey - if (data.activityNodes && data.activityNodes.length > 0) { - // 找到当前节点的索引 - const currentNodeIndex = data.activityNodes.findIndex((node: any) => node.id === activityId) - const nextNode = data.activityNodes[currentNodeIndex + 1] - // 情况一:发起人选择审批人:此时一般是因为条件发生变化,需要当前审批人补充选择 - if ( - nextNode.candidateStrategy === CandidateStrategy.START_USER_SELECT && - !nextNode.tasks && - nextNode.candidateUsers?.length === 0 - ) { - // 自选审批人,则弹出选择审批人弹窗 - // TODO @小北:需要考虑下,这里的 nextNode 可能是多个节点,需要怎么处理;类似你在后端的处理哈 - // TODO @小北:有点纠结,是不是写个预测下一个节点的接口,更合适? - nextAssigneesActivityNode.value = [nextNode] + if (data && data.length > 0) { + data.forEach((node: any) => { + if ( + node.candidateStrategy === CandidateStrategy.START_USER_SELECT && + node.candidateUsers && node.task + ) { + nextAssigneesActivityNode.value.push(node) + } + }) + if (nextAssigneesActivityNode.value.length > 0) { nextAssigneesVisible.value = true } - // TODO @小北:情况二:审批人选择的情况 + + // // 自选审批人,则弹出选择审批人弹窗 + // // TODO @小北:需要考虑下,这里的 nextNode 可能是多个节点,需要怎么处理;类似你在后端的处理哈 + // // TODO @小北:有点纠结,是不是写个预测下一个节点的接口,更合适? + // nextAssigneesActivityNode.value = [nextNode] + // nextAssigneesVisible.value = true + // } + // // TODO @小北:情况二:审批人选择的情况 } } From c2e99fb427ef3c8977a07d1efd7100b5326f06cc Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 3 Mar 2025 14:38:29 +0800 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E6=9D=A1=EF=BC=8CcandidateStrategy=3D34=EF=BC=8C?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/processInstance/detail/ProcessInstanceTimeline.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue b/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue index 30afe69c..110b8bd6 100644 --- a/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue +++ b/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue @@ -43,7 +43,8 @@ v-if=" isEmpty(activity.tasks) && isEmpty(activity.candidateUsers) && - CandidateStrategy.START_USER_SELECT === activity.candidateStrategy + (CandidateStrategy.START_USER_SELECT === activity.candidateStrategy || + CandidateStrategy.APPROVE_USER_SELECT === activity.candidateStrategy) " > @@ -252,7 +253,7 @@ const nodeTypeSvgMap = { // 并行分支节点 [NodeType.PARALLEL_BRANCH_NODE]: { color: '#14bb83', svg: parallelSvg }, // 子流程节点 - [NodeType.CHILD_PROCESS_NODE]: { color: '#14bb83', svg: childProcessSvg }, + [NodeType.CHILD_PROCESS_NODE]: { color: '#14bb83', svg: childProcessSvg } } // 只有只有状态是 -1、0、1 才展示头像右小角状态小icon From 3b5a0ae2904551f616eeca09dbbc944f9018e704 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 3 Mar 2025 21:09:59 +0800 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20=E6=B5=81=E7=A8=8B=E5=86=8D=E6=AC=A1?= =?UTF-8?q?=E5=8F=91=E8=B5=B7=EF=BC=8C=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../create/ProcessDefinitionDetail.vue | 11 ++++++-- .../detail/ProcessInstanceOperationButton.vue | 28 +++++++------------ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue b/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue index 8856fac7..b7a4dbb1 100644 --- a/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue +++ b/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue @@ -117,6 +117,7 @@ const activityNodes = ref([]) // 审批 /** 设置表单信息、获取流程图数据 **/ const initProcessInfo = async (row: any, formVariables?: any) => { + // 重置指定审批人 startUserSelectTasks.value = [] startUserSelectAssignees.value = {} @@ -138,9 +139,13 @@ const initProcessInfo = async (row: any, formVariables?: any) => { await nextTick() fApi.value?.btn.show(false) // 隐藏提交按钮 - // 获取流程审批信息 - await getApprovalDetail(row) - + // 获取流程审批信息,当再次发起时,流程审批节点要根据原始表单参数预测出来 + const param = { + id: row.id, + processVariablesStr: JSON.stringify(formVariables) + } + await getApprovalDetail(param) + // } // 加载流程图 const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id) if (processDefinitionDetail) { diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue index 1c0e8919..af0f75fa 100644 --- a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue +++ b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue @@ -44,7 +44,7 @@ :rows="4" /> - +
() const signRef = ref() const approveSignFormRef = ref() -const nextAssigneesVisible = ref(false) // 是否显示下一个节点的审批人 const nextAssigneesActivityNode = ref([]) // 下一个审批节点信息 const approveReasonForm = reactive({ reason: '', @@ -711,7 +711,7 @@ const closePopover = (type: string, formRef: FormInstance | undefined) => { formRef.resetFields() } popOverVisible.value[type] = false - nextAssigneesVisible.value = false + nextAssigneesActivityNode.value = [] } /** 流程通过时,根据表单变量查询新的流程节点,判断下一个节点类型是否为自选审批人 */ @@ -726,23 +726,14 @@ const initNextAssigneesFormField = async () => { if (data && data.length > 0) { data.forEach((node: any) => { if ( - node.candidateStrategy === CandidateStrategy.START_USER_SELECT && - node.candidateUsers && node.task + isEmpty(node.tasks) && + isEmpty(node.candidateUsers) && + (CandidateStrategy.START_USER_SELECT === node.candidateStrategy || + CandidateStrategy.APPROVE_USER_SELECT === node.candidateStrategy) ) { nextAssigneesActivityNode.value.push(node) } }) - if (nextAssigneesActivityNode.value.length > 0) { - nextAssigneesVisible.value = true - } - - // // 自选审批人,则弹出选择审批人弹窗 - // // TODO @小北:需要考虑下,这里的 nextNode 可能是多个节点,需要怎么处理;类似你在后端的处理哈 - // // TODO @小北:有点纠结,是不是写个预测下一个节点的接口,更合适? - // nextAssigneesActivityNode.value = [nextNode] - // nextAssigneesVisible.value = true - // } - // // TODO @小北:情况二:审批人选择的情况 } } @@ -767,7 +758,8 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) => if (pass) { // 如果需要自选审批人,则校验自选审批人 - if (nextAssigneesVisible.value && Object.keys(approveReasonForm.nextAssignees).length === 0) { + if (Object.keys(nextAssigneesActivityNode.value).length > 0 + && Object.keys(approveReasonForm.nextAssignees).length === 0) { message.warning('下一个节点的审批人不能为空!') return } @@ -793,7 +785,7 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) => } await TaskApi.approveTask(data) popOverVisible.value.approve = false - nextAssigneesVisible.value = false + nextAssigneesActivityNode.value = [] message.success('审批通过成功') } else { // 审批不通过数据 From 5b6616a144f7e03aebf96378a7ead3bba5d82705 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:57:41 +0800 Subject: [PATCH 4/7] =?UTF-8?q?review:=20=E4=BF=AE=E6=94=B9=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/bpm/processInstance/index.ts | 10 +++++----- .../detail/ProcessInstanceOperationButton.vue | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api/bpm/processInstance/index.ts b/src/api/bpm/processInstance/index.ts index 06d0e1b9..06392bc4 100644 --- a/src/api/bpm/processInstance/index.ts +++ b/src/api/bpm/processInstance/index.ts @@ -93,6 +93,11 @@ export const getApprovalDetail = async (params: any) => { return await request.get({ url: '/bpm/process-instance/get-approval-detail', params }) } +// 获取下一个执行的流程节点 +export const getNextApprovalNodes = async (params: any) => { + return await request.get({ url: '/bpm/process-instance/get-next-approval-nodes', params }) +} + // 获取表单字段权限 export const getFormFieldsPermission = async (params: any) => { return await request.get({ url: '/bpm/process-instance/get-form-fields-permission', params }) @@ -102,8 +107,3 @@ export const getFormFieldsPermission = async (params: any) => { export const getProcessInstanceBpmnModelView = async (id: string) => { return await request.get({ url: '/bpm/process-instance/get-bpmn-model-view?id=' + id }) } - -// 获取下一个执行的流程节点 -export const getNextFlowNodes = async (params: any) => { - return await request.get({ url: '/bpm/process-instance/get-next-flow-nodes', params }) -} \ No newline at end of file diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue index af0f75fa..37dbc7b6 100644 --- a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue +++ b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue @@ -718,7 +718,7 @@ const closePopover = (type: string, formRef: FormInstance | undefined) => { const initNextAssigneesFormField = async () => { // 获取修改的流程变量, 暂时只支持流程表单 const variables = getUpdatedProcessInstanceVariables() - const data = await ProcessInstanceApi.getNextFlowNodes({ + const data = await ProcessInstanceApi.getNextApprovalNodes({ processInstanceId: props.processInstance.id, taskId: runningTask.value.id, processVariablesStr: JSON.stringify(variables) From 63b8dd5729b86042160585fd191fc663ee7d9973 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:40:51 +0800 Subject: [PATCH 5/7] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SimpleProcessDesignerV2/src/consts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SimpleProcessDesignerV2/src/consts.ts b/src/components/SimpleProcessDesignerV2/src/consts.ts index 97a6b024..f564c3f0 100644 --- a/src/components/SimpleProcessDesignerV2/src/consts.ts +++ b/src/components/SimpleProcessDesignerV2/src/consts.ts @@ -162,7 +162,7 @@ export enum CandidateStrategy { * 指定用户 */ USER = 30, - /** + /** * 审批人自选 */ APPROVE_USER_SELECT = 34, From 3725e9d3523691a3712fa686a5ece94b89b518ab Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:42:02 +0800 Subject: [PATCH 6/7] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/processInstance/create/ProcessDefinitionDetail.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue b/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue index b7a4dbb1..689c556d 100644 --- a/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue +++ b/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue @@ -140,11 +140,10 @@ const initProcessInfo = async (row: any, formVariables?: any) => { fApi.value?.btn.show(false) // 隐藏提交按钮 // 获取流程审批信息,当再次发起时,流程审批节点要根据原始表单参数预测出来 - const param = { + await getApprovalDetail({ id: row.id, processVariablesStr: JSON.stringify(formVariables) - } - await getApprovalDetail(param) + }) // } // 加载流程图 const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id) From 8244050ea0ae7639cecce5942f40282bd48f78fc Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 17:31:45 +0800 Subject: [PATCH 7/7] =?UTF-8?q?review=EF=BC=9A=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/ProcessInstanceOperationButton.vue | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue index 37dbc7b6..74dfbb32 100644 --- a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue +++ b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue @@ -44,7 +44,11 @@ :rows="4" /> - +
{ }) if (data && data.length > 0) { data.forEach((node: any) => { + // 如果是发起人自选,并且没有审批人 或者 是审批人自选 if ( - isEmpty(node.tasks) && - isEmpty(node.candidateUsers) && - (CandidateStrategy.START_USER_SELECT === node.candidateStrategy || - CandidateStrategy.APPROVE_USER_SELECT === node.candidateStrategy) + (isEmpty(node.tasks) && + isEmpty(node.candidateUsers) && + CandidateStrategy.START_USER_SELECT === node.candidateStrategy) || + CandidateStrategy.APPROVE_USER_SELECT === node.candidateStrategy ) { nextAssigneesActivityNode.value.push(node) } @@ -741,6 +746,20 @@ const initNextAssigneesFormField = async () => { const selectNextAssigneesConfirm = (id: string, userList: any[]) => { approveReasonForm.nextAssignees[id] = userList?.map((item: any) => item.id) } +/** 审批通过时,校验每个自选审批人的节点是否都已配置了审批人 */ +const validateNextAssignees = () => { + // 如果需要自选审批人,则校验自选审批人 + if (Object.keys(nextAssigneesActivityNode.value).length > 0) { + // 校验每个节点是否都已配置审批人 + for (const item of nextAssigneesActivityNode.value) { + if (isEmpty(approveReasonForm.nextAssignees[item.id])) { + message.warning('下一个节点的审批人不能为空!') + return false + } + } + } + return true +} /** 处理审批通过和不通过的操作 */ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) => { @@ -757,12 +776,8 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) => } if (pass) { - // 如果需要自选审批人,则校验自选审批人 - if (Object.keys(nextAssigneesActivityNode.value).length > 0 - && Object.keys(approveReasonForm.nextAssignees).length === 0) { - message.warning('下一个节点的审批人不能为空!') - return - } + const nextAssigneesValid = validateNextAssignees() + if (!nextAssigneesValid) return const variables = getUpdatedProcessInstanceVariables() // 审批通过数据 const data = {