Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
0e7dbbb0
authored
May 26, 2024
by
jason
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
仿钉钉流程设计器- 审批节点配置新增拒绝处理方式
parent
142b0f72
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
190 additions
and
41 deletions
+190
-41
src/components/SimpleProcessDesignerV2/src/NodeHandler.vue
+10
-2
src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue
+70
-25
src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue
+14
-0
src/components/SimpleProcessDesignerV2/src/consts.ts
+15
-6
src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue
+44
-4
src/components/SimpleProcessDesignerV2/src/nodes/ExclusiveNode.vue
+27
-3
src/components/SimpleProcessDesignerV2/src/nodes/UserTaskNode.vue
+10
-1
No files found.
src/components/SimpleProcessDesignerV2/src/NodeHandler.vue
View file @
0e7dbbb0
<
template
>
<div
class=
"node-handler-wrapper"
>
<div
class=
"node-handler"
v-if=
"props.showAdd"
>
<el-popover
trigger=
"hover"
v-model:visible=
"popoverShow"
placement=
"right-start"
width=
"auto"
>
<el-popover
trigger=
"hover"
v-model:visible=
"popoverShow"
placement=
"right-start"
width=
"auto"
>
<div
class=
"handler-item-wrapper"
>
<div
class=
"handler-item"
@
click=
"addNode(NodeType.USER_TASK_NODE)"
>
<div
class=
"approve handler-item-icon"
>
...
...
@@ -31,7 +36,7 @@
</template>
<
script
setup
lang=
"ts"
>
import
{
SimpleFlowNode
,
NodeType
,
NODE_DEFAULT_NAME
,
ApproveMethodType
,
CandidateStrategy
}
from
'./consts'
import
{
SimpleFlowNode
,
NodeType
,
NODE_DEFAULT_NAME
,
ApproveMethodType
,
RejectHandlerType
,
CandidateStrategy
}
from
'./consts'
import
{
generateUUID
}
from
'@/utils'
defineOptions
({
name
:
'NodeHandler'
...
...
@@ -71,6 +76,9 @@ const addNode = (type: number) => {
// 超时处理
timeoutHandler
:
{
enable
:
false
},
rejectHandler
:
{
type
:
RejectHandlerType
.
TERMINATION
}
},
childNode
:
props
.
childNode
...
...
src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue
View file @
0e7dbbb0
...
...
@@ -2,57 +2,102 @@
<!-- 开始节点 -->
<StartEventNode
v-if=
"currentNode && currentNode.type === NodeType.START_EVENT_NODE"
:flow-node =
"currentNode"
@
update:model-value=
"handleModelValueUpdate"
/>
:flow-node=
"currentNode"
@
update:model-value=
"handleModelValueUpdate"
/>
<!-- 审批节点 -->
<UserTaskNode
v-if=
"currentNode && currentNode.type === NodeType.USER_TASK_NODE"
:flow-node =
"currentNode"
@
update:model-value=
"handleModelValueUpdate"
/>
:flow-node=
"currentNode"
@
update:model-value=
"handleModelValueUpdate"
@
find:parent-node=
"findFromParentNode"
/>
<!-- 抄送节点 -->
<CopyTaskNode
v-if=
"currentNode && currentNode.type === NodeType.COPY_TASK_NODE"
:flow-node =
"currentNode"
@
update:model-value=
"handleModelValueUpdate"
/>
:flow-node=
"currentNode"
@
update:model-value=
"handleModelValueUpdate"
/>
<!-- 条件节点 -->
<ExclusiveNode
v-if=
"currentNode && currentNode.type === NodeType.EXCLUSIVE_NODE"
:flow-node =
"currentNode"
@
update:model-value=
"handleModelValueUpdate"
/>
:flow-node=
"currentNode"
@
update:model-value=
"handleModelValueUpdate"
@
find:parent-node=
"findFromParentNode"
/>
<!-- 递归显示孩子节点 -->
<ProcessNodeTree
v-if=
"currentNode && currentNode.childNode"
v-model:flow-node=
"currentNode.childNode"
/>
<ProcessNodeTree
v-if=
"currentNode && currentNode.childNode"
v-model:flow-node=
"currentNode.childNode"
:parent-node=
"currentNode"
@
find:recursive-find-parent-node=
"recursiveFindParentNode"
/>
<!-- 结束节点 -->
<EndEventNode
v-if=
"currentNode && currentNode.type === NodeType.END_EVENT_NODE"
/>
<EndEventNode
v-if=
"currentNode && currentNode.type === NodeType.END_EVENT_NODE"
/>
</
template
>
<
script
setup
lang=
'ts'
>
import
StartEventNode
from
'./nodes/StartEventNode.vue'
;
import
EndEventNode
from
'./nodes/EndEventNode.vue'
;
import
UserTaskNode
from
'./nodes/UserTaskNode.vue'
;
import
CopyTaskNode
from
'./nodes/CopyTaskNode.vue'
;
import
ExclusiveNode
from
'./nodes/ExclusiveNode.vue'
;
import
{
SimpleFlowNode
,
NodeType
}
from
'./consts'
;
<
script
setup
lang=
"ts"
>
import
StartEventNode
from
'./nodes/StartEventNode.vue'
import
EndEventNode
from
'./nodes/EndEventNode.vue'
import
UserTaskNode
from
'./nodes/UserTaskNode.vue'
import
CopyTaskNode
from
'./nodes/CopyTaskNode.vue'
import
ExclusiveNode
from
'./nodes/ExclusiveNode.vue'
import
{
SimpleFlowNode
,
NodeType
}
from
'./consts'
defineOptions
({
name
:
'ProcessNodeTree'
})
const
props
=
defineProps
({
flowNode
:
{
parentNode
:
{
type
:
Object
as
()
=>
SimpleFlowNode
,
default
:
()
=>
null
},
flowNode
:
{
type
:
Object
as
()
=>
SimpleFlowNode
,
default
:
()
=>
null
}
})
const
emits
=
defineEmits
([
'update:flowNode'
])
const
emits
=
defineEmits
<
{
'update:flowNode'
,
'find:recursiveFindParentNode'
:
[
nodeList
:
SimpleFlowNode
[],
curentNode
:
SimpleFlowNode
,
nodeType
:
number
]
}
>
()
const
currentNode
=
ref
<
SimpleFlowNode
>
(
props
.
flowNode
);
const
currentNode
=
ref
<
SimpleFlowNode
>
(
props
.
flowNode
)
// 重要:监控节点变化. 重新绘制节点
watch
(()
=>
props
.
flowNode
,
(
newValue
)
=>
{
currentNode
.
value
=
newValue
;
}
);
watch
(
()
=>
props
.
flowNode
,
(
newValue
)
=>
{
currentNode
.
value
=
newValue
}
)
const
handleModelValueUpdate
=
(
updateValue
)
=>
{
console
.
log
(
'Process Node Tree handleModelValueUpdate'
,
updateValue
)
emits
(
'update:flowNode'
,
updateValue
)
;
emits
(
'update:flowNode'
,
updateValue
)
}
</
script
>
<
style
lang=
'scss'
scoped
>
</
style
>
const
findFromParentNode
=
(
nodeList
:
SimpleFlowNode
[],
nodeType
:
number
)
=>
{
emits
(
'find:recursiveFindParentNode'
,
nodeList
,
props
.
parentNode
,
nodeType
)
}
// 递归从父节点中查询匹配的节点
const
recursiveFindParentNode
=
(
nodeList
:
SimpleFlowNode
[],
findNode
:
SimpleFlowNode
,
nodeType
:
number
)
=>
{
if
(
!
findNode
||
findNode
.
type
===
NodeType
.
START_EVENT_NODE
)
{
return
}
if
(
findNode
.
type
===
nodeType
)
{
nodeList
.
push
(
findNode
)
}
emits
(
'find:recursiveFindParentNode'
,
nodeList
,
props
.
parentNode
,
nodeType
)
}
</
script
>
<
style
lang=
"scss"
scoped
></
style
>
src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue
View file @
0e7dbbb0
...
...
@@ -55,6 +55,18 @@ const processNodeTree = ref<SimpleFlowNode>({
}
})
// const rootNode = ref
<
SimpleFlowNode
>
({
// name: '开始',
// type: NodeType.START_EVENT_NODE,
// id: 'StartEvent_1'
// })
// const childNode = ref
<
SimpleFlowNode
>
({
// id: 'EndEvent_1',
// name: '结束',
// type: NodeType.END_EVENT_NODE
// })
const
errorDialogVisible
=
ref
(
false
)
let
errorNodes
:
SimpleFlowNode
[]
=
[]
const
saveSimpleFlowModel
=
async
()
=>
{
...
...
@@ -148,6 +160,8 @@ onMounted(async () => {
if
(
result
)
{
console
.
log
(
'the result is '
,
result
)
processNodeTree
.
value
=
result
// rootNode.value = result
// childNode.value = result.childNode
}
})
</
script
>
src/components/SimpleProcessDesignerV2/src/consts.ts
View file @
0e7dbbb0
...
...
@@ -62,6 +62,17 @@ export enum TimeUnitType {
DAY
=
3
}
export
enum
RejectHandlerType
{
/**
* 结束流程
*/
TERMINATION
=
1
,
/**
* 驳回到指定节点
*/
RETURN_PRE_USER_TASK
=
2
}
// 条件配置类型 ( 用于条件节点配置 )
export
enum
ConditionConfigType
{
...
...
@@ -186,12 +197,6 @@ NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
NODE_DEFAULT_NAME
.
set
(
NodeType
.
COPY_TASK_NODE
,
'抄送人'
)
NODE_DEFAULT_NAME
.
set
(
NodeType
.
CONDITION_NODE
,
'条件'
)
export
const
TIME_UNIT_MAP
=
new
Map
<
number
,
string
>
()
NODE_DEFAULT_NAME
.
set
(
1
,
'M'
)
NODE_DEFAULT_NAME
.
set
(
NodeType
.
COPY_TASK_NODE
,
'抄送人'
)
NODE_DEFAULT_NAME
.
set
(
NodeType
.
CONDITION_NODE
,
'条件'
)
export
const
APPROVE_METHODS
:
DictDataVO
[]
=
[
{
label
:
'单人审批'
,
value
:
1
},
{
label
:
'多人会签(需所有审批人同意)'
,
value
:
2
},
...
...
@@ -216,6 +221,10 @@ export const TIMEOUT_HANDLER_ACTION_TYPES: DictDataVO [] = [
{
label
:
'自动同意'
,
value
:
2
},
{
label
:
'自动拒绝'
,
value
:
3
},
]
export
const
REJECT_HANDLER_TYPES
:
DictDataVO
[]
=
[
{
label
:
'结束流程'
,
value
:
RejectHandlerType
.
TERMINATION
},
{
label
:
'驳回到指定节点'
,
value
:
RejectHandlerType
.
RETURN_PRE_USER_TASK
}
]
// 比较运算符
export
const
COMPARISON_OPERATORS
:
DictDataVO
=
[
...
...
src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue
View file @
0e7dbbb0
...
...
@@ -131,7 +131,6 @@
/>
</el-select>
</el-form-item>
<el-form-item
v-if=
"currentNode.attributes.candidateStrategy === CandidateStrategy.EXPRESSION"
label=
"流程表达式"
...
...
@@ -144,7 +143,6 @@
style=
"width: 100%"
/>
</el-form-item>
<el-form-item
label=
"审批方式"
prop=
"approveMethod"
>
<el-radio-group
v-model=
"currentNode.attributes.approveMethod"
>
<div
class=
"flex-col"
>
...
...
@@ -163,8 +161,35 @@
</div>
</el-radio-group>
</el-form-item>
<el-divider
content-position=
"left"
>
审批人拒绝时
</el-divider>
<el-form-item
label=
"处理方式"
prop=
"rejectHandler"
>
<el-radio-group
v-model=
"currentNode.attributes.rejectHandler.type"
@
change=
"rejectHandlerTypeChange"
>
<el-radio
:border=
"true"
v-for=
"item in REJECT_HANDLER_TYPES"
:key=
"item.value"
:value=
"item.value"
:label=
"item.label"
/>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"超时处理"
prop=
"timeoutHandlerEnable"
>
<el-form-item
v-if=
"currentNode.attributes.rejectHandler.type == RejectHandlerType.RETURN_PRE_USER_TASK"
label=
"驳回节点"
prop=
"rejectHandlerNode"
>
<el-select
v-model=
"currentNode.attributes.rejectHandler.returnNodeId"
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>
<el-divider
content-position=
"left"
>
审批人超时未处理时
</el-divider>
<el-form-item
label=
"启用开关"
prop=
"timeoutHandlerEnable"
>
<el-switch
v-model=
"currentNode.attributes.timeoutHandler.enable"
active-text=
"开启"
...
...
@@ -281,8 +306,10 @@ import {
NodeType
,
ApproveMethodType
,
TimeUnitType
,
RejectHandlerType
,
TIMEOUT_HANDLER_ACTION_TYPES
,
TIME_UNIT_TYPES
,
REJECT_HANDLER_TYPES
,
NODE_DEFAULT_NAME
}
from
'../consts'
import
{
DICT_TYPE
,
getIntDictOptions
}
from
'@/utils/dict'
...
...
@@ -303,6 +330,9 @@ const props = defineProps({
required
:
true
}
})
const
emits
=
defineEmits
<
{
'find:returnTaskNodes'
:
[
nodeList
:
SimpleFlowNode
[]]
}
>
()
const
notAllowedMultiApprovers
=
ref
(
false
)
const
currentNode
=
ref
<
SimpleFlowNode
>
(
props
.
flowNode
)
...
...
@@ -316,7 +346,7 @@ const deptTreeOptions = inject('deptTree') // 部门树
const
formType
=
inject
(
'formType'
)
// 表单类型
const
formFields
=
inject
<
Ref
<
string
[]
>>
(
'formFields'
)
const
candidateParamArray
=
ref
<
any
[]
>
([])
const
returnTaskList
=
ref
<
SimpleFlowNode
[]
>
([])
const
closeDrawer
=
()
=>
{
settingVisible
.
value
=
false
}
...
...
@@ -443,6 +473,10 @@ const setCurrentNode = (node: SimpleFlowNode) => {
timeDuration
.
value
=
parseInt
(
parseTime
)
timeUnit
.
value
=
convertTimeUnit
(
parseTimeUnit
)
}
// 查找可以驳回的用户节点
const
matchNodeList
=
[];
emits
(
'find:returnTaskNodes'
,
matchNodeList
);
returnTaskList
.
value
=
matchNodeList
;
}
defineExpose
({
open
,
setCurrentNode
})
// 暴露方法给父组件
...
...
@@ -483,6 +517,12 @@ const blurEvent = () => {
currentNode
.
value
.
name
=
currentNode
.
value
.
name
||
(
NODE_DEFAULT_NAME
.
get
(
NodeType
.
USER_TASK_NODE
)
as
string
)
}
const
rejectHandlerTypeChange
=
()
=>
{
if
(
currentNode
.
value
.
attributes
?.
rejectHandler
.
type
===
RejectHandlerType
.
RETURN_PRE_USER_TASK
)
{
console
.
log
(
'nodeList is {}'
,
returnTaskList
.
value
);
}
}
// 默认 6小时
const
timeDuration
=
ref
(
6
)
const
timeUnit
=
ref
(
TimeUnitType
.
HOUR
)
...
...
src/components/SimpleProcessDesignerV2/src/nodes/ExclusiveNode.vue
View file @
0e7dbbb0
...
...
@@ -57,7 +57,11 @@
</div>
<ConditionNodeConfig
:node-index=
"index"
:condition-node=
"item"
:ref=
"item.id"
/>
<!-- 递归显示子节点 -->
<ProcessNodeTree
v-if=
"item && item.childNode"
v-model:flow-node=
"item.childNode"
/>
<ProcessNodeTree
v-if=
"item && item.childNode"
:parent-node=
"item"
v-model:flow-node=
"item.childNode"
@
find:recursive-find-parent-node=
"recursiveFindParentNode"
/>
</div>
</div>
<NodeHandler
v-if=
"currentNode"
v-model:child-node=
"currentNode.childNode"
/>
...
...
@@ -76,6 +80,10 @@ defineOptions({
name
:
'ExclusiveNode'
})
const
props
=
defineProps
({
// parentNode : {
// type: Object as () => SimpleFlowNode,
// required: true
// },
flowNode
:
{
type
:
Object
as
()
=>
SimpleFlowNode
,
required
:
true
...
...
@@ -83,7 +91,9 @@ const props = defineProps({
})
// 定义事件,更新父组件
const
emits
=
defineEmits
<
{
'update:modelValue'
:
[
node
:
SimpleFlowNode
|
undefined
]
'update:modelValue'
:
[
node
:
SimpleFlowNode
|
undefined
],
'find:parentNode'
:
[
nodeList
:
SimpleFlowNode
[],
nodeType
:
number
],
'find:recursiveFindParentNode'
:
[
nodeList
:
SimpleFlowNode
[],
curentNode
:
SimpleFlowNode
,
nodeType
:
number
]
}
>
()
const
currentNode
=
ref
<
SimpleFlowNode
>
(
props
.
flowNode
)
...
...
@@ -156,7 +166,21 @@ const moveNode = (index: number, to: number) => {
}
}
// 递归从父节点中查询匹配的节点
const
recursiveFindParentNode
=
(
nodeList
:
SimpleFlowNode
[],
node
:
SimpleFlowNode
,
nodeType
:
number
)
=>
{
if
(
!
node
||
node
.
type
===
NodeType
.
START_EVENT_NODE
)
{
return
}
if
(
node
.
type
===
nodeType
)
{
nodeList
.
push
(
node
)
}
// 条件节点 (NodeType.CONDITION_NODE) 比较特殊。需要调用其父节点条件分支节点(NodeType.EXCLUSIVE_NODE) 继续查找
emits
(
'find:parentNode'
,
nodeList
,
nodeType
)
}
</
script
>
...
...
src/components/SimpleProcessDesignerV2/src/nodes/UserTaskNode.vue
View file @
0e7dbbb0
...
...
@@ -38,6 +38,7 @@
v-if=
"currentNode"
ref=
"nodeSetting"
:flow-node=
"currentNode"
@
find:return-task-nodes=
"findReturnTaskNodes"
/>
</
template
>
<
script
setup
lang=
"ts"
>
...
...
@@ -55,7 +56,8 @@ const props = defineProps({
}
})
const
emits
=
defineEmits
<
{
'update:modelValue'
:
[
node
:
SimpleFlowNode
|
undefined
]
'update:modelValue'
:
[
node
:
SimpleFlowNode
|
undefined
],
'find:parentNode'
:
[
nodeList
:
SimpleFlowNode
[],
nodeType
:
NodeType
]
}
>
()
const
currentNode
=
ref
<
SimpleFlowNode
>
(
props
.
flowNode
)
...
...
@@ -106,5 +108,12 @@ const copyNode = () => {
currentNode
.
value
=
newCopyNode
emits
(
'update:modelValue'
,
currentNode
.
value
)
}
// 查找可以驳回用户节点
const
findReturnTaskNodes
=
(
matchNodeList
:
SimpleFlowNode
[],
// 匹配的节点
)
=>
{
// 从父节点查找
emits
(
'find:parentNode'
,
matchNodeList
,
NodeType
.
USER_TASK_NODE
);
}
</
script
>
<
style
lang=
"scss"
scoped
></
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment