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
137b33e7
authored
Oct 28, 2024
by
jason
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【功能新增】 仿钉钉流程模型增加浏览模式
parent
d477b35f
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
436 additions
and
59 deletions
+436
-59
src/components/SimpleProcessDesignerV2/src/NodeHandler.vue
+3
-0
src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue
+1
-1
src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue
+2
-1
src/components/SimpleProcessDesignerV2/src/SimpleProcessViewer.vue
+63
-0
src/components/SimpleProcessDesignerV2/src/consts.ts
+3
-1
src/components/SimpleProcessDesignerV2/src/index.ts
+2
-2
src/components/SimpleProcessDesignerV2/src/node.ts
+24
-0
src/components/SimpleProcessDesignerV2/src/nodes/CopyTaskNode.vue
+9
-5
src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue
+15
-1
src/components/SimpleProcessDesignerV2/src/nodes/ExclusiveNode.vue
+21
-6
src/components/SimpleProcessDesignerV2/src/nodes/InclusiveNode.vue
+12
-4
src/components/SimpleProcessDesignerV2/src/nodes/ParallelNode.vue
+7
-14
src/components/SimpleProcessDesignerV2/src/nodes/StartUserNode.vue
+14
-4
src/components/SimpleProcessDesignerV2/src/nodes/UserTaskNode.vue
+16
-5
src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss
+80
-14
src/views/bpm/processInstance/detail/ProcessInstanceSimpleViewer.vue
+160
-0
src/views/bpm/simpleWorkflow/index.vue
+4
-1
No files found.
src/components/SimpleProcessDesignerV2/src/NodeHandler.vue
View file @
137b33e7
...
...
@@ -6,6 +6,7 @@
v-model:visible=
"popoverShow"
placement=
"right-start"
width=
"auto"
v-if=
"!readonly"
>
<div
class=
"handler-item-wrapper"
>
<div
class=
"handler-item"
@
click=
"addNode(NodeType.USER_TASK_NODE)"
>
...
...
@@ -78,6 +79,8 @@ const props = defineProps({
const
emits
=
defineEmits
([
'update:childNode'
])
const
readonly
=
inject
<
Boolean
>
(
'readonly'
)
// 是否只读
const
addNode
=
(
type
:
number
)
=>
{
popoverShow
.
value
=
false
if
(
type
===
NodeType
.
USER_TASK_NODE
)
{
...
...
src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue
View file @
137b33e7
...
...
@@ -47,7 +47,7 @@
/>
<!-- 结束节点 -->
<EndEventNode
v-if=
"currentNode && currentNode.type === NodeType.END_EVENT_NODE"
/>
<EndEventNode
v-if=
"currentNode && currentNode.type === NodeType.END_EVENT_NODE"
:flow-node=
"currentNode"
/>
</
template
>
<
script
setup
lang=
"ts"
>
import
StartUserNode
from
'./nodes/StartUserNode.vue'
...
...
src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue
View file @
137b33e7
...
...
@@ -57,7 +57,7 @@ const props = defineProps({
required
:
true
}
})
const
loading
=
ref
(
tru
e
)
const
loading
=
ref
(
fals
e
)
const
formFields
=
ref
<
string
[]
>
([])
const
formType
=
ref
(
20
)
const
roleOptions
=
ref
<
RoleApi
.
RoleVO
[]
>
([])
// 角色列表
...
...
@@ -66,6 +66,7 @@ const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const
deptOptions
=
ref
<
DeptApi
.
DeptVO
[]
>
([])
// 部门列表
const
deptTreeOptions
=
ref
()
const
userGroupOptions
=
ref
<
UserGroupApi
.
UserGroupVO
[]
>
([])
// 用户组列表
provide
(
'readonly'
,
false
)
provide
(
'formFields'
,
formFields
)
provide
(
'formType'
,
formType
)
provide
(
'roleList'
,
roleOptions
)
...
...
src/components/SimpleProcessDesignerV2/src/SimpleProcessViewer.vue
0 → 100644
View file @
137b33e7
<
template
>
<div
class=
"simple-flow-canvas"
v-loading=
"loading"
>
<div
class=
"simple-flow-container"
>
<div
class=
"scale-container"
:style=
"`transform: scale($
{scaleValue / 100});`">
<ProcessNodeTree
v-if=
"processNodeTree"
v-model:flow-node=
"processNodeTree"
/>
</div>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
ProcessNodeTree
from
'./ProcessNodeTree.vue'
import
{
SimpleFlowNode
}
from
'./consts'
defineOptions
({
name
:
'SimpleProcessRender'
})
const
props
=
defineProps
({
flowNode
:
{
type
:
Object
as
()
=>
SimpleFlowNode
,
required
:
true
}
})
const
loading
=
ref
(
false
)
watch
(
()
=>
props
.
flowNode
,
(
newValue
)
=>
{
processNodeTree
.
value
=
newValue
}
)
const
processNodeTree
=
ref
<
SimpleFlowNode
|
undefined
>
(
props
.
flowNode
)
provide
(
'readonly'
,
true
)
let
scaleValue
=
ref
(
100
)
const
MAX_SCALE_VALUE
=
200
const
MIN_SCALE_VALUE
=
50
// 放大
const
zoomOut
=
()
=>
{
if
(
scaleValue
.
value
==
MAX_SCALE_VALUE
)
{
return
}
scaleValue
.
value
+=
10
}
// 缩小
const
zoomIn
=
()
=>
{
if
(
scaleValue
.
value
==
MIN_SCALE_VALUE
)
{
return
}
scaleValue
.
value
-=
10
}
// onMounted(async () => {
// try {
// loading.value = true
// if (props.view) {
// processNodeTree.value = props.view.simpleModel
// }
// } finally {
// loading.value = false
// }
// })
</
script
>
src/components/SimpleProcessDesignerV2/src/consts.ts
View file @
137b33e7
// @ts-ignore
import
{
DictDataVO
}
from
'@/api/system/dict/types'
import
{
TaskStatusEnum
}
from
'@/api/bpm/task'
/**
* 节点类型
*/
...
...
@@ -96,6 +96,8 @@ export interface SimpleFlowNode {
conditionGroups
?:
ConditionGroup
// 是否默认的条件
defaultFlow
?:
boolean
// 活动的状态,用于前端节点状态展示
activityStatus
?
:
TaskStatusEnum
}
// 候选人策略枚举 ( 用于审批节点。抄送节点 )
export
enum
CandidateStrategy
{
...
...
src/components/SimpleProcessDesignerV2/src/index.ts
View file @
137b33e7
import
SimpleProcessDesigner
from
'./SimpleProcessDesigner.vue'
import
SimpleProcessViewer
from
'./SimpleProcessViewer.vue'
import
'../theme/simple-process-designer.scss'
export
{
SimpleProcessDesigner
}
\ No newline at end of file
export
{
SimpleProcessDesigner
,
SimpleProcessViewer
}
src/components/SimpleProcessDesignerV2/src/node.ts
View file @
137b33e7
import
{
cloneDeep
}
from
'lodash-es'
import
{
TaskStatusEnum
}
from
'@/api/bpm/task'
import
*
as
RoleApi
from
'@/api/system/role'
import
*
as
DeptApi
from
'@/api/system/dept'
import
*
as
PostApi
from
'@/api/system/post'
...
...
@@ -476,3 +477,26 @@ export function useNodeName2(node: Ref<SimpleFlowNode>, nodeType: NodeType) {
blurEvent
}
}
/**
* @description 根据节点任务状态,获取节点任务状态样式
*/
export
function
useTaskStatusClass
(
taskStatus
:
TaskStatusEnum
|
undefined
)
:
string
{
if
(
!
taskStatus
)
{
return
''
}
if
(
taskStatus
===
TaskStatusEnum
.
APPROVE
)
{
return
'status-pass'
}
if
(
taskStatus
===
TaskStatusEnum
.
RUNNING
)
{
return
'status-running'
}
if
(
taskStatus
===
TaskStatusEnum
.
REJECT
)
{
return
'status-reject'
}
if
(
taskStatus
===
TaskStatusEnum
.
CANCEL
)
{
return
'status-cancel'
}
return
''
;
}
src/components/SimpleProcessDesignerV2/src/nodes/CopyTaskNode.vue
View file @
137b33e7
...
...
@@ -5,7 +5,7 @@
<div
class=
"node-title-container"
>
<div
class=
"node-title-icon copy-task"
><span
class=
"iconfont icon-copy"
></span></div>
<input
v-if=
"showInput"
v-if=
"
!readonly &&
showInput"
type=
"text"
class=
"editable-title-input"
@
blur=
"blurEvent()"
...
...
@@ -24,9 +24,9 @@
<div
class=
"node-text"
v-else
>
{{
NODE_DEFAULT_TEXT
.
get
(
NodeType
.
COPY_TASK_NODE
)
}}
</div>
<Icon
icon=
"ep:arrow-right-bold"
/>
<Icon
v-if=
"!readonly"
icon=
"ep:arrow-right-bold"
/>
</div>
<div
class=
"node-toolbar"
>
<div
v-if=
"!readonly"
class=
"node-toolbar"
>
<div
class=
"toolbar-icon"
><Icon
color=
"#0089ff"
icon=
"ep:circle-close-filled"
:size=
"18"
@
click=
"deleteNode"
/></div>
...
...
@@ -36,7 +36,7 @@
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
<NodeHandler
v-if=
"currentNode"
v-model:child-node=
"currentNode.childNode"
/>
</div>
<CopyTaskNodeConfig
v-if=
"currentNode"
ref=
"nodeSetting"
:flow-node=
"currentNode"
/>
<CopyTaskNodeConfig
v-if=
"
!readonly &&
currentNode"
ref=
"nodeSetting"
:flow-node=
"currentNode"
/>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
...
...
@@ -57,7 +57,8 @@ const props = defineProps({
const
emits
=
defineEmits
<
{
'update:flowNode'
:
[
node
:
SimpleFlowNode
|
undefined
]
}
>
()
// 是否只读
const
readonly
=
inject
<
Boolean
>
(
'readonly'
)
// 监控节点的变化
const
currentNode
=
useWatchNode
(
props
)
// 节点名称编辑
...
...
@@ -66,6 +67,9 @@ const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.
const
nodeSetting
=
ref
()
// 打开节点配置
const
openNodeConfig
=
()
=>
{
if
(
readonly
)
{
return
}
nodeSetting
.
value
.
showCopyTaskNodeConfig
(
currentNode
.
value
)
nodeSetting
.
value
.
openDrawer
()
}
...
...
src/components/SimpleProcessDesignerV2/src/nodes/EndEventNode.vue
View file @
137b33e7
<
template
>
<div
class=
"end-node-wrapper"
>
<div
class=
"end-node-box"
>
<div
class=
"end-node-box"
:class=
"taskStatusClass"
>
<span
class=
"node-fixed-name"
title=
"结束"
>
结束
</span>
</div>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
SimpleFlowNode
}
from
'../consts'
import
{
useWatchNode
,
useTaskStatusClass
}
from
'../node'
defineOptions
({
name
:
'EndEventNode'
})
const
props
=
defineProps
({
flowNode
:
{
type
:
Object
as
()
=>
SimpleFlowNode
,
default
:
()
=>
null
}
})
// 监控节点变化
const
currentNode
=
useWatchNode
(
props
)
// 节点任务状态样式
const
taskStatusClass
=
useTaskStatusClass
(
currentNode
.
value
?.
activityStatus
)
</
script
>
<
style
lang=
"scss"
scoped
></
style
>
src/components/SimpleProcessDesignerV2/src/nodes/ExclusiveNode.vue
View file @
137b33e7
<
template
>
<div
class=
"branch-node-wrapper"
>
<div
class=
"branch-node-container"
>
<el-button
class=
"branch-node-add"
color=
"#67c23a"
@
click=
"addCondition"
plain
>
添加条件
</el-button>
<div
v-if=
"readonly"
class=
"branch-node-readonly"
:class=
"taskStatusClass"
>
<span
class=
"iconfont icon-exclusive icon-size"
></span>
</div>
<el-button
v-else
class=
"branch-node-add"
color=
"#67c23a"
@
click=
"addCondition"
plain
>
添加条件
</el-button
>
<div
class=
"branch-node-item"
v-for=
"(item, index) in currentNode.conditionNodes"
...
...
@@ -17,9 +23,9 @@
</
template
>
<div
class=
"node-wrapper"
>
<div
class=
"node-container"
>
<div
class=
"node-box"
:class=
"
{ 'node-config-error': !item.showText }
"
>
<div
class=
"node-box"
:class=
"
[{ 'node-config-error': !item.showText }, `${useTaskStatusClass(item.activityStatus)}`]
"
>
<div
class=
"branch-node-title-container"
>
<div
v-if=
"showInputs[index]"
>
<div
v-if=
"
!readonly &&
showInputs[index]"
>
<input
type=
"text"
class=
"input-max-width editable-title-input"
...
...
@@ -39,7 +45,10 @@
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div>
</div>
<div
class=
"node-toolbar"
v-if=
"index + 1 !== currentNode.conditionNodes?.length"
>
<div
class=
"node-toolbar"
v-if=
"!readonly && index + 1 !== currentNode.conditionNodes?.length"
>
<div
class=
"toolbar-icon"
>
<Icon
color=
"#0089ff"
...
...
@@ -87,6 +96,7 @@ import NodeHandler from '../NodeHandler.vue'
import
ProcessNodeTree
from
'../ProcessNodeTree.vue'
import
{
SimpleFlowNode
,
NodeType
,
NODE_DEFAULT_TEXT
}
from
'../consts'
import
{
getDefaultConditionNodeName
}
from
'../utils'
import
{
useTaskStatusClass
}
from
'../node'
import
{
generateUUID
}
from
'@/utils'
import
ConditionNodeConfig
from
'../nodes-config/ConditionNodeConfig.vue'
const
{
proxy
}
=
getCurrentInstance
()
as
any
...
...
@@ -109,9 +119,11 @@ const emits = defineEmits<{
nodeType
:
number
]
}
>
()
// 是否只读
const
readonly
=
inject
<
Boolean
>
(
'readonly'
)
const
currentNode
=
ref
<
SimpleFlowNode
>
(
props
.
flowNode
)
// const conditionNodes = computed(() => currentNode.value.conditionNodes);
// 节点状态样式
const
taskStatusClass
=
useTaskStatusClass
(
currentNode
.
value
?.
activityStatus
)
watch
(
()
=>
props
.
flowNode
,
...
...
@@ -135,6 +147,9 @@ const clickEvent = (index: number) => {
}
const
conditionNodeConfig
=
(
nodeId
:
string
)
=>
{
if
(
readonly
)
{
return
}
const
conditionNode
=
proxy
.
$refs
[
nodeId
][
0
]
conditionNode
.
open
()
}
...
...
src/components/SimpleProcessDesignerV2/src/nodes/InclusiveNode.vue
View file @
137b33e7
<
template
>
<div
class=
"branch-node-wrapper"
>
<div
class=
"branch-node-container"
>
<el-button
class=
"branch-node-add"
color=
"#345da2"
@
click=
"addCondition"
plain
>
添加条件
</el-button>
<div
v-if=
"readonly"
class=
"branch-node-readonly"
>
<span
class=
"iconfont icon-inclusive icon-size"
></span>
</div>
<el-button
v-else
class=
"branch-node-add"
color=
"#345da2"
@
click=
"addCondition"
plain
>
添加条件
</el-button>
<div
class=
"branch-node-item"
v-for=
"(item, index) in currentNode.conditionNodes"
...
...
@@ -38,7 +41,7 @@
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div>
</div>
<div
class=
"node-toolbar"
v-if=
"index + 1 !== currentNode.conditionNodes?.length"
>
<div
class=
"node-toolbar"
v-if=
"
!readonly &&
index + 1 !== currentNode.conditionNodes?.length"
>
<div
class=
"toolbar-icon"
>
<Icon
color=
"#0089ff"
...
...
@@ -50,7 +53,7 @@
</div>
<div
class=
"branch-node-move move-node-left"
v-if=
"index != 0 && index + 1 !== currentNode.conditionNodes?.length"
v-if=
"
!readonly &&
index != 0 && index + 1 !== currentNode.conditionNodes?.length"
@
click=
"moveNode(index, -1)"
>
<Icon
icon=
"ep:arrow-left"
/>
...
...
@@ -58,7 +61,7 @@
<div
class=
"branch-node-move move-node-right"
v-if=
"currentNode.conditionNodes && index < currentNode.conditionNodes.length - 2"
v-if=
"
!readonly &&
currentNode.conditionNodes && index < currentNode.conditionNodes.length - 2"
@
click=
"moveNode(index, 1)"
>
<Icon
icon=
"ep:arrow-right"
/>
...
...
@@ -108,6 +111,8 @@ const emits = defineEmits<{
nodeType
:
number
]
}
>
()
// 是否只读
const
readonly
=
inject
<
Boolean
>
(
'readonly'
)
const
currentNode
=
ref
<
SimpleFlowNode
>
(
props
.
flowNode
)
...
...
@@ -133,6 +138,9 @@ const clickEvent = (index: number) => {
}
const
conditionNodeConfig
=
(
nodeId
:
string
)
=>
{
if
(
readonly
)
{
return
}
const
conditionNode
=
proxy
.
$refs
[
nodeId
][
0
]
conditionNode
.
open
()
}
...
...
src/components/SimpleProcessDesignerV2/src/nodes/ParallelNode.vue
View file @
137b33e7
<
template
>
<div
class=
"branch-node-wrapper"
>
<div
class=
"branch-node-container"
>
<el-button
class=
"branch-node-add"
color=
"#626aef"
@
click=
"addCondition"
plain
>
添加分支
</el-button>
<div
v-if=
"readonly"
class=
"branch-node-readonly"
>
<span
class=
"iconfont icon-parallel icon-size"
></span>
</div>
<el-button
v-else
class=
"branch-node-add"
color=
"#626aef"
@
click=
"addCondition"
plain
>
添加分支
</el-button>
<div
class=
"branch-node-item"
v-for=
"(item, index) in currentNode.conditionNodes"
...
...
@@ -39,7 +42,7 @@
{{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
</div>
</div>
<div
class=
"node-toolbar"
>
<div
v-if=
"!readonly"
class=
"node-toolbar"
>
<div
class=
"toolbar-icon"
>
<Icon
color=
"#0089ff"
...
...
@@ -49,18 +52,6 @@
/>
</div>
</div>
<!-- <div
class="branch-node-move move-node-left"
v-if="index != 0 && index + 1 !== currentNode.conditionNodes?.length" @click="moveNode(index, -1)">
<Icon icon="ep:arrow-left" />
</div> -->
<!-- <div
class="branch-node-move move-node-right"
v-if="currentNode.conditionNodes && index < currentNode.conditionNodes.length - 2"
@click="moveNode(index, 1)">
<Icon icon="ep:arrow-right" />
</div> -->
</div>
<NodeHandler
v-model:child-node=
"item.childNode"
/>
</div>
...
...
@@ -106,6 +97,8 @@ const emits = defineEmits<{
}
>
()
const
currentNode
=
ref
<
SimpleFlowNode
>
(
props
.
flowNode
)
// 是否只读
const
readonly
=
inject
<
Boolean
>
(
'readonly'
)
watch
(
()
=>
props
.
flowNode
,
...
...
src/components/SimpleProcessDesignerV2/src/nodes/StartUserNode.vue
View file @
137b33e7
<
template
>
<div
class=
"node-wrapper"
>
<div
class=
"node-container"
>
<div
class=
"node-box"
:class=
"
{ 'node-config-error': !currentNode.showText }">
<div
class=
"node-box"
:class=
"[
{ 'node-config-error': !currentNode.showText }, `${taskStatusClass}`]"
>
<div
class=
"node-title-container"
>
<div
class=
"node-title-icon start-user"
><span
class=
"iconfont icon-start-user"
></span
...
...
@@ -26,18 +29,18 @@
<div
class=
"node-text"
v-else
>
{{
NODE_DEFAULT_TEXT
.
get
(
NodeType
.
START_USER_NODE
)
}}
</div>
<Icon
icon=
"ep:arrow-right-bold"
/>
<Icon
icon=
"ep:arrow-right-bold"
v-if=
"!readonly"
/>
</div>
</div>
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
<NodeHandler
v-if=
"currentNode"
v-model:child-node=
"currentNode.childNode"
/>
</div>
</div>
<StartUserNodeConfig
v-if=
"currentNode"
ref=
"nodeSetting"
:flow-node=
"currentNode"
/>
<StartUserNodeConfig
v-if=
"
!readonly &&
currentNode"
ref=
"nodeSetting"
:flow-node=
"currentNode"
/>
</
template
>
<
script
setup
lang=
"ts"
>
import
NodeHandler
from
'../NodeHandler.vue'
import
{
useWatchNode
,
useNodeName2
}
from
'../node'
import
{
useWatchNode
,
useNodeName2
,
useTaskStatusClass
}
from
'../node'
import
{
SimpleFlowNode
,
NODE_DEFAULT_TEXT
,
NodeType
}
from
'../consts'
import
StartUserNodeConfig
from
'../nodes-config/StartUserNodeConfig.vue'
defineOptions
({
...
...
@@ -49,21 +52,28 @@ const props = defineProps({
default
:
()
=>
null
}
})
const
readonly
=
inject
<
Boolean
>
(
'readonly'
)
// 是否只读
// 定义事件,更新父组件。
const
emits
=
defineEmits
<
{
'update:modelValue'
:
[
node
:
SimpleFlowNode
|
undefined
]
}
>
()
// 监控节点变化
const
currentNode
=
useWatchNode
(
props
)
// 节点任务状态样式
const
taskStatusClass
=
useTaskStatusClass
(
currentNode
.
value
?.
activityStatus
)
// 节点名称编辑
const
{
showInput
,
blurEvent
,
clickTitle
}
=
useNodeName2
(
currentNode
,
NodeType
.
START_USER_NODE
)
const
nodeSetting
=
ref
()
// 打开节点配置
const
openNodeConfig
=
()
=>
{
if
(
readonly
)
{
return
}
// 把当前节点传递给配置组件
nodeSetting
.
value
.
showStartUserNodeConfig
(
currentNode
.
value
)
nodeSetting
.
value
.
openDrawer
()
}
</
script
>
<
style
lang=
"scss"
scoped
></
style
>
src/components/SimpleProcessDesignerV2/src/nodes/UserTaskNode.vue
View file @
137b33e7
<
template
>
<div
class=
"node-wrapper"
>
<div
class=
"node-container"
>
<div
class=
"node-box"
:class=
"
{ 'node-config-error': !currentNode.showText }">
<div
class=
"node-box"
:class=
"[
{ 'node-config-error': !currentNode.showText }, `${taskStatusClass}`]"
>
<div
class=
"node-title-container"
>
<div
class=
"node-title-icon user-task"
><span
class=
"iconfont icon-approve"
></span></div>
<input
v-if=
"showInput"
v-if=
"
!readonly &&
showInput"
type=
"text"
class=
"editable-title-input"
@
blur=
"blurEvent()"
...
...
@@ -24,9 +27,9 @@
<div
class=
"node-text"
v-else
>
{{
NODE_DEFAULT_TEXT
.
get
(
NodeType
.
USER_TASK_NODE
)
}}
</div>
<Icon
icon=
"ep:arrow-right-bold"
/>
<Icon
icon=
"ep:arrow-right-bold"
v-if=
"!readonly"
/>
</div>
<div
class=
"node-toolbar"
>
<div
v-if=
"!readonly"
class=
"node-toolbar"
>
<div
class=
"toolbar-icon"
><Icon
color=
"#0089ff"
icon=
"ep:circle-close-filled"
:size=
"18"
@
click=
"deleteNode"
/></div>
...
...
@@ -45,7 +48,7 @@
</
template
>
<
script
setup
lang=
"ts"
>
import
{
SimpleFlowNode
,
NodeType
,
NODE_DEFAULT_TEXT
}
from
'../consts'
import
{
useWatchNode
,
useNodeName2
}
from
'../node'
import
{
useWatchNode
,
useNodeName2
,
useTaskStatusClass
}
from
'../node'
import
NodeHandler
from
'../NodeHandler.vue'
import
UserTaskNodeConfig
from
'../nodes-config/UserTaskNodeConfig.vue'
defineOptions
({
...
...
@@ -61,13 +64,21 @@ const emits = defineEmits<{
'update:flowNode'
:
[
node
:
SimpleFlowNode
|
undefined
]
'find:parentNode'
:
[
nodeList
:
SimpleFlowNode
[],
nodeType
:
NodeType
]
}
>
()
// 是否只读
const
readonly
=
inject
<
Boolean
>
(
'readonly'
)
// 监控节点变化
const
currentNode
=
useWatchNode
(
props
)
// 节点状态样式
const
taskStatusClass
=
useTaskStatusClass
(
currentNode
.
value
?.
activityStatus
)
// 节点名称编辑
const
{
showInput
,
blurEvent
,
clickTitle
}
=
useNodeName2
(
currentNode
,
NodeType
.
START_USER_NODE
)
const
nodeSetting
=
ref
()
// 打开节点配置
const
openNodeConfig
=
()
=>
{
if
(
readonly
)
{
return
}
// 把当前节点传递给配置组件
nodeSetting
.
value
.
showUserTaskNodeConfig
(
currentNode
.
value
)
nodeSetting
.
value
.
openDrawer
()
...
...
src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss
View file @
137b33e7
.simple-flow-canvas
{
position
:
absolute
;
inset
:
0
;
z-index
:
1
;
overflow
:
auto
;
background-color
:
#fafafa
;
user-select
:
none
;
//
user-select: none;
.simple-flow-container
{
position
:
relative
;
...
...
@@ -84,12 +82,32 @@
background-color
:
#fff
;
flex-direction
:
column
;
border
:
2px
solid
transparent
;
// border-color: #0089ff;
border-radius
:
8px
;
// border-color: #0089ff;
box-shadow
:
0
1px
4px
0
rgba
(
10
,
30
,
65
,
0
.16
);
transition
:
all
0
.1s
cubic-bezier
(
0
.645
,
0
.045
,
0
.355
,
1
);
&
.status-pass
{
border-color
:
#67c23a
;
background-color
:
#a9da90
;
}
&
.status-pass
:hover
{
border-color
:
#67c23a
;
}
&
.status-running
{
border-color
:
#5a9cf8
;
background-color
:
#e7f0fe
;
}
&
.status-running
:hover
{
border-color
:
#5a9cf8
;
}
&
.status-reject
{
border-color
:
#e47470
;
background-color
:
#f6e5e5
;
}
&
.status-reject
:hover
{
border-color
:
#e47470
;
}
&
:hover
{
border-color
:
#0089ff
;
.node-toolbar
{
...
...
@@ -280,15 +298,10 @@
&
:
:
before
{
position
:
absolute
;
top
:
0
;
right
:
0
;
left
:
0
;
// bottom: 5px;
bottom
:
0px
;
top
:
0
;
z-index
:
0
;
width
:
2px
;
height
:
100%
;
// height: calc(100% - 5px);
margin
:
auto
;
background-color
:
#dedede
;
content
:
''
;
...
...
@@ -361,6 +374,36 @@
transform-origin
:
center
center
;
}
.branch-node-readonly
{
position
:
absolute
;
top
:
-18px
;
left
:
50%
;
z-index
:
1
;
width
:
36px
;
height
:
36px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
border
:
2px
solid
#dedede
;
background-color
:
#fff
;
border-radius
:
50%
;
transform
:
translateX
(
-50%
);
transform-origin
:
center
center
;
&
.status-pass
{
border-color
:
#6bb63c
;
background-color
:
#e9f4e2
;
}
&
.status-pass
:hover
{
border-color
:
#6bb63c
;
}
.icon-size
{
font-size
:
22px
;
color
:
#67c23a
;
}
}
.branch-node-item
{
position
:
relative
;
display
:
flex
;
...
...
@@ -454,7 +497,6 @@
padding
:
3px
4px
;
color
:
#212121
;
cursor
:
pointer
;
// background: #2c2c2c;
background
:
#fafafa
;
border-radius
:
30px
;
box-shadow
:
0
1px
5px
0
rgba
(
10
,
30
,
65
,
0
.08
);
...
...
@@ -473,12 +515,36 @@
align-items
:
center
;
width
:
80px
;
height
:
36px
;
border
:
2px
solid
#fafafa
;
color
:
#212121
;
// background: #6e6e6e;
background
:
#fafafa
;
border-radius
:
30px
;
box-shadow
:
0
1px
5px
0
rgba
(
10
,
30
,
65
,
0
.08
);
box-sizing
:
border-box
;
&
.status-pass
{
border-color
:
#6bb63c
;
background-color
:
#a9da90
;
}
&
.status-pass
:hover
{
border-color
:
#6bb63c
;
}
&
.status-reject
{
border-color
:
#e47470
;
background-color
:
#f6e5e5
;
}
&
.status-reject
:hover
{
border-color
:
#e47470
;
}
&
.status-cancel
{
border-color
:
#919398
;
background-color
:
#eaeaeb
}
&
.status-cancel
:hover
{
border-color
:
#919398
;
}
}
}
...
...
src/views/bpm/processInstance/detail/ProcessInstanceSimpleViewer.vue
0 → 100644
View file @
137b33e7
<
template
>
<el-card
v-loading=
"loading"
class=
"box-card"
>
<SimpleProcessViewer
:flow-node=
"simpleModel"
/>
</el-card>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
TaskStatusEnum
}
from
'@/api/bpm/task'
import
{
BpmProcessInstanceStatus
}
from
'@/utils/constants'
import
{
SimpleFlowNode
,
NodeType
}
from
'@/components/SimpleProcessDesignerV2/src/consts'
import
{
SimpleProcessViewer
}
from
'@/components/SimpleProcessDesignerV2/src/'
import
*
as
ProcessInstanceApi
from
'@/api/bpm/processInstance'
defineOptions
({
name
:
'BpmProcessInstanceSimpleViewer'
})
const
props
=
defineProps
({
loading
:
propTypes
.
bool
.
def
(
false
),
// 是否加载中
id
:
propTypes
.
string
// 流程实例的编号
})
// const view = ref({
// simpleModel: undefined
// }) // simple 模型数据
const
simpleModel
=
ref
()
/** 只有 loading 完成时,才去加载流程列表 */
watch
(
()
=>
props
.
loading
,
async
(
value
)
=>
{
if
(
value
&&
props
.
id
)
{
const
modelView
=
await
ProcessInstanceApi
.
getProcessInstanceBpmnModelView
(
props
.
id
)
if
(
modelView
)
{
// 已经拒绝的活动节点编号集合,只包括 UserTask
const
rejectedTaskActivityIds
:
string
[]
=
modelView
.
rejectedTaskActivityIds
// 进行中的活动节点编号集合, 只包括 UserTask
const
unfinishedTaskActivityIds
:
string
[]
=
modelView
.
unfinishedTaskActivityIds
// 已经完成的活动节点编号集合, 包括 UserTask、Gateway 等
const
finishedActivityIds
:
string
[]
=
modelView
.
finishedTaskActivityIds
// 已经完成的连线节点编号集合,只包括 SequenceFlow
const
finishedSequenceFlowActivityIds
:
string
[]
=
modelView
.
finishedSequenceFlowActivityIds
setSimpleModelNodeTaskStatus
(
modelView
.
simpleModel
,
modelView
.
processInstance
.
status
,
rejectedTaskActivityIds
,
unfinishedTaskActivityIds
,
finishedActivityIds
,
finishedSequenceFlowActivityIds
)
console
.
log
(
"modelView.simpleModel==>"
,
modelView
.
simpleModel
)
simpleModel
.
value
=
modelView
.
simpleModel
}
}
}
)
const
setSimpleModelNodeTaskStatus
=
(
simpleModel
:
SimpleFlowNode
|
undefined
,
processStatus
:
number
,
rejectedTaskActivityIds
:
string
[],
unfinishedTaskActivityIds
:
string
[],
finishedActivityIds
:
string
[],
finishedSequenceFlowActivityIds
:
string
[],
)
=>
{
if
(
!
simpleModel
)
{
return
}
// 结束节点
if
(
simpleModel
.
type
===
NodeType
.
END_EVENT_NODE
)
{
if
(
finishedActivityIds
.
includes
(
simpleModel
.
id
))
{
simpleModel
.
activityStatus
=
processStatus
}
else
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
NOT_START
}
return
}
// 审批节点
if
(
simpleModel
.
type
===
NodeType
.
START_USER_NODE
||
simpleModel
.
type
===
NodeType
.
USER_TASK_NODE
)
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
NOT_START
if
(
rejectedTaskActivityIds
.
includes
(
simpleModel
.
id
))
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
REJECT
}
else
if
(
unfinishedTaskActivityIds
.
includes
(
simpleModel
.
id
))
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
RUNNING
}
else
if
(
finishedActivityIds
.
includes
(
simpleModel
.
id
))
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
APPROVE
}
// TODO 是不是还缺一个 cancel 的状态
}
// 抄送节点
if
(
simpleModel
.
type
===
NodeType
.
COPY_TASK_NODE
)
{
// 抄送节点 只有通过和未执行状态
if
(
finishedActivityIds
.
includes
(
simpleModel
.
id
))
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
APPROVE
}
else
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
NOT_START
}
}
// 条件节点 对应 SequenceFlow
if
(
simpleModel
.
type
===
NodeType
.
CONDITION_NODE
)
{
// 条件节点。只有通过和未执行状态
if
(
finishedSequenceFlowActivityIds
.
includes
(
simpleModel
.
id
))
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
APPROVE
}
else
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
NOT_START
}
}
// 网关节点
if
(
simpleModel
.
type
===
NodeType
.
CONDITION_BRANCH_NODE
||
simpleModel
.
type
===
NodeType
.
PARALLEL_BRANCH_NODE
||
simpleModel
.
type
===
NodeType
.
INCLUSIVE_BRANCH_NODE
)
{
// 网关节点。只有通过和未执行状态
if
(
finishedActivityIds
.
includes
(
simpleModel
.
id
))
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
APPROVE
}
else
{
simpleModel
.
activityStatus
=
TaskStatusEnum
.
NOT_START
}
simpleModel
.
conditionNodes
?.
forEach
((
node
)
=>
{
setSimpleModelNodeTaskStatus
(
node
,
processStatus
,
rejectedTaskActivityIds
,
unfinishedTaskActivityIds
,
finishedActivityIds
,
finishedSequenceFlowActivityIds
)
})
}
setSimpleModelNodeTaskStatus
(
simpleModel
.
childNode
,
processStatus
,
rejectedTaskActivityIds
,
unfinishedTaskActivityIds
,
finishedActivityIds
,
finishedSequenceFlowActivityIds
)
}
/** 监听 bpmnXml */
// watch(
// () => props.bpmnXml,
// (value) => {
// view.value.bpmnXml = value
// }
// )
</
script
>
<
style
>
.box-card
{
width
:
100%
;
margin-bottom
:
20px
;
}
</
style
>
src/views/bpm/simpleWorkflow/index.vue
View file @
137b33e7
<
template
>
<SimpleProcessDesigner
:model-id=
"modelId"
/>
<ContentWrap
:bodyStyle=
"
{ padding: '0px 0px' }" class="position-relative">
<SimpleProcessDesigner
:model-id=
"modelId"
/>
</ContentWrap>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
SimpleProcessDesigner
}
from
'@/components/SimpleProcessDesignerV2/src/'
...
...
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