【功能优化】条件分支、包容分支后面不允许直接添加并行分支

This commit is contained in:
YunaiV 2024-11-02 09:11:19 +08:00
parent e504be7d42
commit a097560f81
7 changed files with 227 additions and 171 deletions

View File

@ -27,14 +27,7 @@
</div> </div>
<div class="handler-item-text">条件分支</div> <div class="handler-item-text">条件分支</div>
</div> </div>
<div <div class="handler-item" @click="addNode(NodeType.PARALLEL_BRANCH_NODE)">
class="handler-item"
@click="addNode(NodeType.PARALLEL_BRANCH_NODE)"
v-if="
NodeType.CONDITION_BRANCH_NODE !== currentNode?.type &&
NodeType.INCLUSIVE_BRANCH_NODE !== currentNode?.type
"
>
<div class="handler-item-icon parallel"> <div class="handler-item-icon parallel">
<span class="iconfont icon-size icon-parallel"></span> <span class="iconfont icon-size icon-parallel"></span>
</div> </div>
@ -70,8 +63,10 @@ import { generateUUID } from '@/utils'
defineOptions({ defineOptions({
name: 'NodeHandler' name: 'NodeHandler'
}) })
const popoverShow = ref(false)
const message = useMessage() //
const popoverShow = ref(false)
const props = defineProps({ const props = defineProps({
childNode: { childNode: {
type: Object as () => SimpleFlowNode, type: Object as () => SimpleFlowNode,
@ -87,6 +82,17 @@ const emits = defineEmits(['update:childNode'])
const readonly = inject<Boolean>('readonly') // const readonly = inject<Boolean>('readonly') //
const addNode = (type: number) => { const addNode = (type: number) => {
//
if (
type === NodeType.PARALLEL_BRANCH_NODE &&
[NodeType.CONDITION_BRANCH_NODE, NodeType.INCLUSIVE_BRANCH_NODE].includes(
props.currentNode?.type
)
) {
message.error('条件分支、包容分支后面,不允许直接添加并行分支')
return
}
popoverShow.value = false popoverShow.value = false
if (type === NodeType.USER_TASK_NODE) { if (type === NodeType.USER_TASK_NODE) {
const id = 'Activity_' + generateUUID() const id = 'Activity_' + generateUUID()

View File

@ -3,7 +3,10 @@
<div class="node-container"> <div class="node-container">
<div <div
class="node-box" class="node-box"
:class="[{ 'node-config-error': !currentNode.showText }, `${useTaskStatusClass(currentNode?.activityStatus)}`]" :class="[
{ 'node-config-error': !currentNode.showText },
`${useTaskStatusClass(currentNode?.activityStatus)}`
]"
> >
<div class="node-title-container"> <div class="node-title-container">
<div class="node-title-icon copy-task"><span class="iconfont icon-copy"></span></div> <div class="node-title-icon copy-task"><span class="iconfont icon-copy"></span></div>
@ -37,7 +40,11 @@
</div> </div>
<!-- 传递子节点给添加节点组件会在子节点前面添加节点 --> <!-- 传递子节点给添加节点组件会在子节点前面添加节点 -->
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" :current-node="currentNode"/> <NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div> </div>
<CopyTaskNodeConfig <CopyTaskNodeConfig
v-if="!readonly && currentNode" v-if="!readonly && currentNode"

View File

@ -1,7 +1,11 @@
<template> <template>
<div class="branch-node-wrapper"> <div class="branch-node-wrapper">
<div class="branch-node-container"> <div class="branch-node-container">
<div v-if="readonly" class="branch-node-readonly" :class="`${useTaskStatusClass(currentNode?.activityStatus)}`"> <div
v-if="readonly"
class="branch-node-readonly"
:class="`${useTaskStatusClass(currentNode?.activityStatus)}`"
>
<span class="iconfont icon-exclusive icon-size condition"></span> <span class="iconfont icon-exclusive icon-size condition"></span>
</div> </div>
<el-button v-else class="branch-node-add" color="#67c23a" @click="addCondition" plain <el-button v-else class="branch-node-add" color="#67c23a" @click="addCondition" plain
@ -23,7 +27,13 @@
</template> </template>
<div class="node-wrapper"> <div class="node-wrapper">
<div class="node-container"> <div class="node-container">
<div class="node-box" :class="[{ 'node-config-error': !item.showText }, `${useTaskStatusClass(item.activityStatus)}`]"> <div
class="node-box"
:class="[
{ 'node-config-error': !item.showText },
`${useTaskStatusClass(item.activityStatus)}`
]"
>
<div class="branch-node-title-container"> <div class="branch-node-title-container">
<div v-if="!readonly && showInputs[index]"> <div v-if="!readonly && showInputs[index]">
<input <input
@ -87,7 +97,11 @@
/> />
</div> </div>
</div> </div>
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" :current-node="currentNode" /> <NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div> </div>
</template> </template>
@ -120,7 +134,7 @@ const emits = defineEmits<{
] ]
}>() }>()
// //
const readonly = inject<Boolean>('readonly') const readonly = inject<Boolean>('readonly')
const currentNode = ref<SimpleFlowNode>(props.flowNode) const currentNode = ref<SimpleFlowNode>(props.flowNode)
watch( watch(
() => props.flowNode, () => props.flowNode,

View File

@ -1,10 +1,16 @@
<template> <template>
<div class="branch-node-wrapper"> <div class="branch-node-wrapper">
<div class="branch-node-container"> <div class="branch-node-container">
<div v-if="readonly" class="branch-node-readonly" :class="`${useTaskStatusClass(currentNode?.activityStatus)}`" > <div
v-if="readonly"
class="branch-node-readonly"
:class="`${useTaskStatusClass(currentNode?.activityStatus)}`"
>
<span class="iconfont icon-inclusive icon-size inclusive"></span> <span class="iconfont icon-inclusive icon-size inclusive"></span>
</div> </div>
<el-button v-else class="branch-node-add" color="#345da2" @click="addCondition" plain>添加条件</el-button> <el-button v-else class="branch-node-add" color="#345da2" @click="addCondition" plain
>添加条件</el-button
>
<div <div
class="branch-node-item" class="branch-node-item"
v-for="(item, index) in currentNode.conditionNodes" v-for="(item, index) in currentNode.conditionNodes"
@ -20,7 +26,13 @@
</template> </template>
<div class="node-wrapper"> <div class="node-wrapper">
<div class="node-container"> <div class="node-container">
<div class="node-box" :class="[{ 'node-config-error': !item.showText }, `${useTaskStatusClass(item.activityStatus)}`]"> <div
class="node-box"
:class="[
{ 'node-config-error': !item.showText },
`${useTaskStatusClass(item.activityStatus)}`
]"
>
<div class="branch-node-title-container"> <div class="branch-node-title-container">
<div v-if="showInputs[index]"> <div v-if="showInputs[index]">
<input <input
@ -41,7 +53,10 @@
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }} {{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div> </div>
</div> </div>
<div class="node-toolbar" v-if="!readonly && index + 1 !== currentNode.conditionNodes?.length"> <div
class="node-toolbar"
v-if="!readonly && index + 1 !== currentNode.conditionNodes?.length"
>
<div class="toolbar-icon"> <div class="toolbar-icon">
<Icon <Icon
color="#0089ff" color="#0089ff"
@ -61,13 +76,17 @@
<div <div
class="branch-node-move move-node-right" class="branch-node-move move-node-right"
v-if="!readonly && currentNode.conditionNodes && index < currentNode.conditionNodes.length - 2" v-if="
!readonly &&
currentNode.conditionNodes &&
index < currentNode.conditionNodes.length - 2
"
@click="moveNode(index, 1)" @click="moveNode(index, 1)"
> >
<Icon icon="ep:arrow-right" /> <Icon icon="ep:arrow-right" />
</div> </div>
</div> </div>
<NodeHandler v-model:child-node="item.childNode" :current-node="item"/> <NodeHandler v-model:child-node="item.childNode" :current-node="item" />
</div> </div>
</div> </div>
<ConditionNodeConfig :node-index="index" :condition-node="item" :ref="item.id" /> <ConditionNodeConfig :node-index="index" :condition-node="item" :ref="item.id" />
@ -80,7 +99,11 @@
/> />
</div> </div>
</div> </div>
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" :current-node="currentNode"/> <NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div> </div>
</template> </template>

View File

@ -1,10 +1,16 @@
<template> <template>
<div class="branch-node-wrapper"> <div class="branch-node-wrapper">
<div class="branch-node-container"> <div class="branch-node-container">
<div v-if="readonly" class="branch-node-readonly" :class="`${useTaskStatusClass(currentNode?.activityStatus)}`"> <div
v-if="readonly"
class="branch-node-readonly"
:class="`${useTaskStatusClass(currentNode?.activityStatus)}`"
>
<span class="iconfont icon-parallel icon-size parallel"></span> <span class="iconfont icon-parallel icon-size parallel"></span>
</div> </div>
<el-button v-else class="branch-node-add" color="#626aef" @click="addCondition" plain>添加分支</el-button> <el-button v-else class="branch-node-add" color="#626aef" @click="addCondition" plain
>添加分支</el-button
>
<div <div
class="branch-node-item" class="branch-node-item"
v-for="(item, index) in currentNode.conditionNodes" v-for="(item, index) in currentNode.conditionNodes"
@ -42,7 +48,7 @@
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }} {{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div> </div>
</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 <Icon
color="#0089ff" color="#0089ff"
@ -53,7 +59,7 @@
</div> </div>
</div> </div>
</div> </div>
<NodeHandler v-model:child-node="item.childNode" :current-node="item"/> <NodeHandler v-model:child-node="item.childNode" :current-node="item" />
</div> </div>
</div> </div>
<!-- 递归显示子节点 --> <!-- 递归显示子节点 -->
@ -65,7 +71,11 @@
/> />
</div> </div>
</div> </div>
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" :current-node="currentNode" /> <NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div> </div>
</template> </template>
@ -98,7 +108,7 @@ const emits = defineEmits<{
const currentNode = ref<SimpleFlowNode>(props.flowNode) const currentNode = ref<SimpleFlowNode>(props.flowNode)
// //
const readonly = inject<Boolean>('readonly') const readonly = inject<Boolean>('readonly')
watch( watch(
() => props.flowNode, () => props.flowNode,

View File

@ -3,7 +3,10 @@
<div class="node-container"> <div class="node-container">
<div <div
class="node-box" class="node-box"
:class="[{ 'node-config-error': !currentNode.showText }, `${useTaskStatusClass(currentNode?.activityStatus)}`]" :class="[
{ 'node-config-error': !currentNode.showText },
`${useTaskStatusClass(currentNode?.activityStatus)}`
]"
> >
<div class="node-title-container"> <div class="node-title-container">
<div class="node-title-icon start-user" <div class="node-title-icon start-user"
@ -33,73 +36,68 @@
</div> </div>
</div> </div>
<!-- 传递子节点给添加节点组件会在子节点前面添加节点 --> <!-- 传递子节点给添加节点组件会在子节点前面添加节点 -->
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" :current-node="currentNode" /> <NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div> </div>
</div> </div>
<StartUserNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" /> <StartUserNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
<!-- 审批记录 --> <!-- 审批记录 -->
<el-dialog :title="dialogTitle || '审批记录'" v-model="dialogVisible" width="1000px" append-to-body> <el-dialog
<el-row> :title="dialogTitle || '审批记录'"
<el-table v-model="dialogVisible"
:data="selectTasks" width="1000px"
size="small" append-to-body
border >
header-cell-class-name="table-header-gray" <el-row>
> <el-table :data="selectTasks" size="small" border header-cell-class-name="table-header-gray">
<el-table-column <el-table-column
label="序号" label="序号"
header-align="center" header-align="center"
align="center" align="center"
type="index" type="index"
width="50" width="50"
/> />
<el-table-column <el-table-column label="审批人" min-width="100" align="center">
label="审批人" <template #default="scope">
min-width="100" {{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }}
align="center" </template>
> </el-table-column>
<template #default="scope">
{{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }} <el-table-column label="部门" min-width="100" align="center">
</template> <template #default="scope">
</el-table-column> {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
</template>
<el-table-column label="部门" min-width="100" align="center"> </el-table-column>
<template #default="scope"> <el-table-column
{{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }} :formatter="dateFormatter"
</template> align="center"
</el-table-column> label="开始时间"
<el-table-column prop="createTime"
:formatter="dateFormatter" min-width="140"
align="center" />
label="开始时间" <el-table-column
prop="createTime" :formatter="dateFormatter"
min-width="140" align="center"
/> label="结束时间"
<el-table-column prop="endTime"
:formatter="dateFormatter" min-width="140"
align="center" />
label="结束时间" <el-table-column align="center" label="审批状态" prop="status" min-width="90">
prop="endTime" <template #default="scope">
min-width="140" <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
/> </template>
<el-table-column align="center" label="审批状态" prop="status" min-width="90"> </el-table-column>
<template #default="scope"> <el-table-column align="center" label="审批建议" prop="reason" min-width="120" />
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" /> <el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
</template> <template #default="scope">
</el-table-column> {{ formatPast2(scope.row.durationInMillis) }}
<el-table-column </template>
align="center" </el-table-column>
label="审批建议" </el-table>
prop="reason" </el-row>
min-width="120"
/>
<el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
<template #default="scope">
{{ formatPast2(scope.row.durationInMillis) }}
</template>
</el-table-column>
</el-table>
</el-row>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -130,13 +128,15 @@ const currentNode = useWatchNode(props)
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.START_USER_NODE) const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.START_USER_NODE)
const nodeSetting = ref() const nodeSetting = ref()
// //
const nodeClick = () => { const nodeClick = () => {
if (readonly) { if (readonly) {
// //
if(tasks && tasks.value){ if (tasks && tasks.value) {
dialogTitle.value = currentNode.value.name dialogTitle.value = currentNode.value.name
selectTasks.value = tasks.value.filter((item: any) => item?.taskDefinitionKey === currentNode.value.id) selectTasks.value = tasks.value.filter(
(item: any) => item?.taskDefinitionKey === currentNode.value.id
)
dialogVisible.value = true dialogVisible.value = true
} }
} else { } else {
@ -144,13 +144,11 @@ const nodeClick = () => {
nodeSetting.value.showStartUserNodeConfig(currentNode.value) nodeSetting.value.showStartUserNodeConfig(currentNode.value)
nodeSetting.value.openDrawer() nodeSetting.value.openDrawer()
} }
} }
// //
const dialogVisible = ref(false) // const dialogVisible = ref(false) //
const dialogTitle = ref<string | undefined>(undefined) // const dialogTitle = ref<string | undefined>(undefined) //
const selectTasks = ref<any[]|undefined>([]) // const selectTasks = ref<any[] | undefined>([]) //
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -3,7 +3,10 @@
<div class="node-container"> <div class="node-container">
<div <div
class="node-box" class="node-box"
:class="[{ 'node-config-error': !currentNode.showText }, `${useTaskStatusClass(currentNode?.activityStatus)}`]" :class="[
{ 'node-config-error': !currentNode.showText },
`${useTaskStatusClass(currentNode?.activityStatus)}`
]"
> >
<div class="node-title-container"> <div class="node-title-container">
<div class="node-title-icon user-task"><span class="iconfont icon-approve"></span></div> <div class="node-title-icon user-task"><span class="iconfont icon-approve"></span></div>
@ -36,7 +39,11 @@
</div> </div>
</div> </div>
<!-- 传递子节点给添加节点组件会在子节点前面添加节点 --> <!-- 传递子节点给添加节点组件会在子节点前面添加节点 -->
<NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" :current-node="currentNode" /> <NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div> </div>
</div> </div>
<UserTaskNodeConfig <UserTaskNodeConfig
@ -46,68 +53,59 @@
@find:return-task-nodes="findReturnTaskNodes" @find:return-task-nodes="findReturnTaskNodes"
/> />
<!-- 审批记录 --> <!-- 审批记录 -->
<el-dialog :title="dialogTitle || '审批记录'" v-model="dialogVisible" width="1000px" append-to-body> <el-dialog
<el-row> :title="dialogTitle || '审批记录'"
<el-table v-model="dialogVisible"
:data="selectTasks" width="1000px"
size="small" append-to-body
border >
header-cell-class-name="table-header-gray" <el-row>
> <el-table :data="selectTasks" size="small" border header-cell-class-name="table-header-gray">
<el-table-column <el-table-column
label="序号" label="序号"
header-align="center" header-align="center"
align="center" align="center"
type="index" type="index"
width="50" width="50"
/> />
<el-table-column <el-table-column label="审批人" min-width="100" align="center">
label="审批人" <template #default="scope">
min-width="100" {{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }}
align="center" </template>
> </el-table-column>
<template #default="scope">
{{ scope.row.assigneeUser?.nickname || scope.row.ownerUser?.nickname }} <el-table-column label="部门" min-width="100" align="center">
</template> <template #default="scope">
</el-table-column> {{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
</template>
<el-table-column label="部门" min-width="100" align="center"> </el-table-column>
<template #default="scope"> <el-table-column
{{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }} :formatter="dateFormatter"
</template> align="center"
</el-table-column> label="开始时间"
<el-table-column prop="createTime"
:formatter="dateFormatter" min-width="140"
align="center" />
label="开始时间" <el-table-column
prop="createTime" :formatter="dateFormatter"
min-width="140" align="center"
/> label="结束时间"
<el-table-column prop="endTime"
:formatter="dateFormatter" min-width="140"
align="center" />
label="结束时间" <el-table-column align="center" label="审批状态" prop="status" min-width="90">
prop="endTime" <template #default="scope">
min-width="140" <dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" />
/> </template>
<el-table-column align="center" label="审批状态" prop="status" min-width="90"> </el-table-column>
<template #default="scope"> <el-table-column align="center" label="审批建议" prop="reason" min-width="120" />
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="scope.row.status" /> <el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
</template> <template #default="scope">
</el-table-column> {{ formatPast2(scope.row.durationInMillis) }}
<el-table-column </template>
align="center" </el-table-column>
label="审批建议" </el-table>
prop="reason" </el-row>
min-width="120"
/>
<el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
<template #default="scope">
{{ formatPast2(scope.row.durationInMillis) }}
</template>
</el-table-column>
</el-table>
</el-row>
</el-dialog> </el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -140,17 +138,18 @@ const currentNode = useWatchNode(props)
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.START_USER_NODE) const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.START_USER_NODE)
const nodeSetting = ref() const nodeSetting = ref()
const nodeClick = () => { const nodeClick = () => {
if (readonly) { if (readonly) {
if(tasks && tasks.value){ if (tasks && tasks.value) {
dialogTitle.value = currentNode.value.name dialogTitle.value = currentNode.value.name
// //
selectTasks.value = tasks.value.filter((item: any) => item?.taskDefinitionKey === currentNode.value.id) selectTasks.value = tasks.value.filter(
(item: any) => item?.taskDefinitionKey === currentNode.value.id
)
dialogVisible.value = true dialogVisible.value = true
} }
} else { } else {
// //
nodeSetting.value.showUserTaskNodeConfig(currentNode.value) nodeSetting.value.showUserTaskNodeConfig(currentNode.value)
nodeSetting.value.openDrawer() nodeSetting.value.openDrawer()
} }
@ -170,7 +169,6 @@ const findReturnTaskNodes = (
// //
const dialogVisible = ref(false) // const dialogVisible = ref(false) //
const dialogTitle = ref<string | undefined>(undefined) // const dialogTitle = ref<string | undefined>(undefined) //
const selectTasks = ref<any[]|undefined>([]) // const selectTasks = ref<any[] | undefined>([]) //
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>