Pre Merge pull request !710 from Lemon/feature/bpm-审核超时跳转任意节点

pull/710/MERGE
Lemon 2025-03-25 04:58:00 +00:00 committed by Gitee
commit f6277ae4a0
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
5 changed files with 194 additions and 38 deletions

View File

@ -81,6 +81,7 @@
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// 导入各种流程节点组件
import StartUserNode from './nodes/StartUserNode.vue' import StartUserNode from './nodes/StartUserNode.vue'
import EndEventNode from './nodes/EndEventNode.vue' import EndEventNode from './nodes/EndEventNode.vue'
import UserTaskNode from './nodes/UserTaskNode.vue' import UserTaskNode from './nodes/UserTaskNode.vue'
@ -94,57 +95,98 @@ import TriggerNode from './nodes/TriggerNode.vue'
import ChildProcessNode from './nodes/ChildProcessNode.vue' import ChildProcessNode from './nodes/ChildProcessNode.vue'
import { SimpleFlowNode, NodeType } from './consts' import { SimpleFlowNode, NodeType } from './consts'
import { useWatchNode } from './node' import { useWatchNode } from './node'
// 定义组件名称
defineOptions({ defineOptions({
name: 'ProcessNodeTree' name: 'ProcessNodeTree'
}) })
// 定义组件的 props
const props = defineProps({ const props = defineProps({
// 父节点对象
parentNode: { parentNode: {
type: Object as () => SimpleFlowNode, type: Object as () => SimpleFlowNode,
default: () => null default: () => null
}, },
// 当前流程节点对象
flowNode: { flowNode: {
type: Object as () => SimpleFlowNode, type: Object as () => SimpleFlowNode,
default: () => null default: () => null
} }
}) })
// 定义组件的事件
const emits = defineEmits<{ const emits = defineEmits<{
// 更新流程节点的事件
'update:flowNode': [node: SimpleFlowNode | undefined] 'update:flowNode': [node: SimpleFlowNode | undefined]
// 递归查找父节点的事件
'find:recursiveFindParentNode': [ 'find:recursiveFindParentNode': [
nodeList: SimpleFlowNode[], nodeList: SimpleFlowNode[], // 节点列表
curentNode: SimpleFlowNode, curentNode: SimpleFlowNode, // 当前节点
nodeType: number nodeType: number // 节点类型
] ]
}>() }>()
// 使用 useWatchNode hook 监听节点变化
const currentNode = useWatchNode(props) const currentNode = useWatchNode(props)
// 用于删除节点 // 处理节点更新事件,用于删除节点等操作
const handleModelValueUpdate = (updateValue) => { const handleModelValueUpdate = (updateValue) => {
emits('update:flowNode', updateValue) emits('update:flowNode', updateValue)
} }
// 从父节点中查找指定类型的节点
const findFromParentNode = (nodeList: SimpleFlowNode[], nodeType: number) => { const findFromParentNode = (nodeList: SimpleFlowNode[], nodeType: number) => {
emits('find:recursiveFindParentNode', nodeList, props.parentNode, nodeType) emits('find:recursiveFindParentNode', nodeList, props.parentNode, nodeType)
} }
// 递归从父节点中查询匹配的节点 // 优化递归查找函数,添加缓存机制
const nodeCache = new Map<string, SimpleFlowNode[]>()
const recursiveFindParentNode = ( const recursiveFindParentNode = (
nodeList: SimpleFlowNode[], nodeList: SimpleFlowNode[],
findNode: SimpleFlowNode, findNode: SimpleFlowNode,
nodeType: number nodeType: number
) => { ) => {
if (!findNode) { if (!findNode) return
return
} // Create a Set to track visited nodes and prevent cycles
if (findNode.type === NodeType.START_USER_NODE) { const visitedNodes = new Set<string>()
nodeList.push(findNode)
return const findNodesRecursive = (node: SimpleFlowNode) => {
if (!node || visitedNodes.has(node.id)) return
visitedNodes.add(node.id)
// Check current node
if (node.type === nodeType) {
if (!nodeList.some(n => n.id === node.id)) {
nodeList.push(node)
}
}
// Check child node
if (node.childNode) {
findNodesRecursive(node.childNode)
}
// Check condition nodes if present
if (node.type === NodeType.CONDITION_BRANCH_NODE && node.conditionNodes) {
node.conditionNodes.forEach(conditionNode => {
if (conditionNode.childNode) {
findNodesRecursive(conditionNode.childNode)
}
})
}
} }
if (findNode.type === nodeType) { // Start recursive search
nodeList.push(findNode) findNodesRecursive(findNode)
// Continue search in parent node
if (props.parentNode) {
emits('find:recursiveFindParentNode', nodeList, props.parentNode, nodeType)
} }
emits('find:recursiveFindParentNode', nodeList, props.parentNode, nodeType)
} }
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -244,6 +244,8 @@ export type TimeoutHandler = {
timeDuration?: string timeDuration?: string
// 执行动作是自动提醒, 最大提醒次数 // 执行动作是自动提醒, 最大提醒次数
maxRemindCount?: number maxRemindCount?: number
// 执行动作是自动跳转时,跳转的节点ID
returnNodeId?: string
} }
/** /**
@ -302,6 +304,16 @@ export enum RejectHandlerType {
*/ */
RETURN_USER_TASK = 2 RETURN_USER_TASK = 2
} }
export enum DelaySettingType {
/**
* 默认开启
*/
FINISH_PROCESS = 1,
/**
* 延迟器指定节点
*/
RETURN_USER_TASK = 2
}
// 用户任务超时处理类型枚举 // 用户任务超时处理类型枚举
export enum TimeoutHandlerType { export enum TimeoutHandlerType {
/** /**
@ -315,7 +327,11 @@ export enum TimeoutHandlerType {
/** /**
* 自动拒绝 * 自动拒绝
*/ */
REJECT = 3 REJECT = 3,
/**
* 自动跳转
*/
AUTO_JUMP = 4
} }
// 用户任务的审批人为空时,处理类型枚举 // 用户任务的审批人为空时,处理类型枚举
export enum AssignEmptyHandlerType { export enum AssignEmptyHandlerType {
@ -583,7 +599,8 @@ export const TIME_UNIT_TYPES: DictDataVO[] = [
export const TIMEOUT_HANDLER_TYPES: DictDataVO[] = [ export const TIMEOUT_HANDLER_TYPES: DictDataVO[] = [
{ label: '自动提醒', value: 1 }, { label: '自动提醒', value: 1 },
{ label: '自动同意', value: 2 }, { label: '自动同意', value: 2 },
{ label: '自动拒绝', value: 3 } { label: '自动拒绝', value: 3 },
{ label: '自动跳转', value: 4 }
] ]
export const REJECT_HANDLER_TYPES: DictDataVO[] = [ export const REJECT_HANDLER_TYPES: DictDataVO[] = [
{ label: '终止流程', value: RejectHandlerType.FINISH_PROCESS }, { label: '终止流程', value: RejectHandlerType.FINISH_PROCESS },
@ -708,11 +725,11 @@ export enum ProcessVariableEnum {
/** /**
* 延迟设置 * 延迟设置
*/ */
export type DelaySetting = { export interface DelaySetting {
// 延迟类型 delayType: DelayTypeEnum
delayType: number
// 延迟时间表达式
delayTime: string delayTime: string
autoJumpEnable?: boolean
returnNodeId?: string
} }
/** /**
* 延迟类型 * 延迟类型

View File

@ -156,6 +156,7 @@ export type UserTaskFormType = {
assignStartUserHandlerType?: AssignStartUserHandlerType assignStartUserHandlerType?: AssignStartUserHandlerType
timeDuration?: number timeDuration?: number
maxRemindCount?: number maxRemindCount?: number
timeoutReturnNodeId?: string
buttonsSetting: any[] buttonsSetting: any[]
taskCreateListenerEnable?: boolean taskCreateListenerEnable?: boolean
taskCreateListenerPath?: string taskCreateListenerPath?: string

View File

@ -347,6 +347,25 @@
> >
<el-input-number v-model="configForm.maxRemindCount" :min="1" :max="10" /> <el-input-number v-model="configForm.maxRemindCount" :min="1" :max="10" />
</el-form-item> </el-form-item>
<el-form-item
v-if="configForm.timeoutHandlerEnable && configForm.timeoutHandlerType === TimeoutHandlerType.AUTO_JUMP"
label="跳转节点"
prop="timeoutReturnNodeId"
>
<el-select
filterable
v-model="configForm.timeoutReturnNodeId"
clearable
style="width: 100%"
>
<el-option
v-for="item in returnTaskList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</div> </div>
<el-divider content-position="left">{{ nodeTypeName }}人为空时</el-divider> <el-divider content-position="left">{{ nodeTypeName }}人为空时</el-divider>
@ -729,7 +748,9 @@ const saveConfig = async () => {
enable: configForm.value.timeoutHandlerEnable!, enable: configForm.value.timeoutHandlerEnable!,
type: cTimeoutType.value, type: cTimeoutType.value,
timeDuration: isoTimeDuration.value, timeDuration: isoTimeDuration.value,
maxRemindCount: cTimeoutMaxRemindCount.value maxRemindCount: cTimeoutMaxRemindCount.value,
returnNodeId: configForm.value.timeoutHandlerType === TimeoutHandlerType.AUTO_JUMP ?
configForm.value.timeoutReturnNodeId : undefined
} }
// 设置审批人为空时 // 设置审批人为空时
currentNode.value.assignEmptyHandler = { currentNode.value.assignEmptyHandler = {
@ -803,15 +824,24 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
returnTaskList.value = matchNodeList returnTaskList.value = matchNodeList
// 2.4 设置审批超时处理 // 2.4 设置审批超时处理
configForm.value.timeoutHandlerEnable = node.timeoutHandler?.enable configForm.value.timeoutHandlerEnable = node.timeoutHandler?.enable
if (node.timeoutHandler?.enable && node.timeoutHandler?.timeDuration) { if (node.timeoutHandler?.enable) {
const strTimeDuration = node.timeoutHandler.timeDuration configForm.value.timeoutHandlerType = node.timeoutHandler.type
let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1) configForm.value.timeoutReturnNodeId = node.timeoutHandler.returnNodeId
let parseTimeUnit = strTimeDuration.slice(strTimeDuration.length - 1)
configForm.value.timeDuration = parseInt(parseTime) if (node.timeoutHandler.timeDuration) {
timeUnit.value = convertTimeUnit(parseTimeUnit) const strTimeDuration = node.timeoutHandler.timeDuration
let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1)
let parseTimeUnit = strTimeDuration.slice(strTimeDuration.length - 1)
configForm.value.timeDuration = parseInt(parseTime)
timeUnit.value = convertTimeUnit(parseTimeUnit)
}
if (node.timeoutHandler.type === TimeoutHandlerType.AUTO_JUMP) {
const matchNodeList: SimpleFlowNode[] = []
emits('find:returnTaskNodes', matchNodeList)
returnTaskList.value = matchNodeList
}
} }
configForm.value.timeoutHandlerType = node.timeoutHandler?.type
configForm.value.maxRemindCount = node.timeoutHandler?.maxRemindCount
// 2.5 设置审批人为空时 // 2.5 设置审批人为空时
configForm.value.assignEmptyHandlerType = node.assignEmptyHandler?.type configForm.value.assignEmptyHandlerType = node.assignEmptyHandler?.type
configForm.value.assignEmptyHandlerUserIds = node.assignEmptyHandler?.userIds configForm.value.assignEmptyHandlerUserIds = node.assignEmptyHandler?.userIds
@ -902,6 +932,11 @@ function useTimeoutHandler() {
const timeoutHandlerTypeChanged = () => { const timeoutHandlerTypeChanged = () => {
if (configForm.value.timeoutHandlerType === TimeoutHandlerType.REMINDER) { if (configForm.value.timeoutHandlerType === TimeoutHandlerType.REMINDER) {
configForm.value.maxRemindCount = 1 // 超时提醒次数,默认为1 configForm.value.maxRemindCount = 1 // 超时提醒次数,默认为1
} else if (configForm.value.timeoutHandlerType === TimeoutHandlerType.AUTO_JUMP) {
// 获取可跳转节点列表
const matchNodeList: SimpleFlowNode[] = []
emits('find:returnTaskNodes', matchNodeList)
returnTaskList.value = matchNodeList
} }
} }

View File

@ -41,7 +41,7 @@
</div> </div>
<div v-if="!readonly" class="node-toolbar"> <div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon" <div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode" ><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div> /></div>
</div> </div>
</div> </div>
@ -147,12 +147,16 @@ const nodeSetting = ref()
const nodeClick = () => { const nodeClick = () => {
if (readonly) { if (readonly) {
console.log('[UserTaskNode] 开始处理只读模式点击, tasks长度:', tasks.value?.length)
if (tasks && tasks.value) { if (tasks && tasks.value) {
dialogTitle.value = currentNode.value.name dialogTitle.value = currentNode.value.name
console.time('过滤任务耗时')
// 只读模式,弹窗显示任务信息 // 只读模式,弹窗显示任务信息
selectTasks.value = tasks.value.filter( selectTasks.value = tasks.value.filter(
(item: any) => item?.taskDefinitionKey === currentNode.value.id (item: any) => item?.taskDefinitionKey === currentNode.value.id
) )
console.timeEnd('过滤任务耗时')
console.log('[UserTaskNode] 过滤后的任务数:', selectTasks.value.length)
dialogVisible.value = true dialogVisible.value = true
} }
} else { } else {
@ -165,12 +169,69 @@ const nodeClick = () => {
const deleteNode = () => { const deleteNode = () => {
emits('update:flowNode', currentNode.value.childNode) emits('update:flowNode', currentNode.value.childNode)
} }
// 查找可以驳回用户节点 // 优化查找可驳回用户节点函数
const findReturnTaskNodes = ( const findReturnTaskNodes = (matchNodeList: SimpleFlowNode[]) => {
matchNodeList: SimpleFlowNode[] // 匹配的节点 console.time('findReturnTaskNodes总耗时')
) => {
// 从父节点查找 // 使用Set进行去重
const uniqueNodes = new Set<SimpleFlowNode>()
const currentNodeId = currentNode.value.id
const processedNodes = new Set<string>() // 用于记录已处理的节点
// 从父节点查找用户任务节点
console.time('父节点查找耗时')
emits('find:parentNode', matchNodeList, NodeType.USER_TASK_NODE) emits('find:parentNode', matchNodeList, NodeType.USER_TASK_NODE)
console.timeEnd('父节点查找耗时')
// 过滤并去重
matchNodeList.forEach(node => {
if (!processedNodes.has(node.id) && node.id !== currentNodeId) {
uniqueNodes.add(node)
processedNodes.add(node.id)
}
})
// 查找子节点
findChildUserTaskNodes(currentNode.value, uniqueNodes, currentNodeId, processedNodes)
// 更新结果数组
matchNodeList.length = 0
matchNodeList.push(...Array.from(uniqueNodes))
console.log('[findReturnTaskNodes] 最终找到的节点数:', matchNodeList.length)
console.timeEnd('findReturnTaskNodes总耗时')
}
// 优化子节点查找函数
const findChildUserTaskNodes = (
node: SimpleFlowNode,
uniqueNodes: Set<SimpleFlowNode>,
currentNodeId: string,
processedNodes: Set<string>
) => {
if (!node || processedNodes.has(node.id)) return
processedNodes.add(node.id)
// 处理直接子节点
if (node.childNode) {
if (node.childNode.type === NodeType.USER_TASK_NODE &&
node.childNode.id !== currentNodeId &&
!processedNodes.has(node.childNode.id)) {
uniqueNodes.add(node.childNode)
processedNodes.add(node.childNode.id)
}
findChildUserTaskNodes(node.childNode, uniqueNodes, currentNodeId, processedNodes)
}
// 处理条件分支节点
if (node.type === NodeType.CONDITION_BRANCH_NODE && node.conditionNodes) {
for (const conditionNode of node.conditionNodes) {
if (conditionNode.childNode) {
findChildUserTaskNodes(conditionNode.childNode, uniqueNodes, currentNodeId, processedNodes)
}
}
}
} }
// 任务的弹窗显示,用于只读模式 // 任务的弹窗显示,用于只读模式