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
Unverified
Commit
12f52b28
authored
Nov 02, 2024
by
芋道源码
Committed by
GitHub
Nov 02, 2024
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #98 from GoldenZqqq/feature/bpm
发起流程页面优化
parents
a6f325b1
a57840c8
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
442 additions
and
2 deletions
+442
-2
src/assets/svgs/bpm/finish.svg
+2
-0
src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue
+278
-0
src/views/bpm/processInstance/create/index_new.vue
+150
-0
src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue
+12
-2
No files found.
src/assets/svgs/bpm/finish.svg
0 → 100644
View file @
12f52b28
<svg
t=
"1730189225011"
class=
"icon"
viewBox=
"0 0 1024 1024"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
p-id=
"2651"
id=
"mx_n_1730189225011"
width=
"200"
height=
"200"
><path
d=
"M793.889347 200.380242c27.648573 20.615681 42.196018 32.710677 63.781037 56.119312 25.313864 27.453234 43.242957 48.52047 64.502857 86.507991 44.537416 79.580127 53.527718 136.949077 53.517684 212.063821 0 64.933675-15.452562 130.459388-40.138263 187.311893-22.076044 50.841799-61.545336 104.359483-101.886297 138.933914-45.506755 39.001681-81.214423 60.462941-137.605337 81.826531-55.699867 21.102023-114.070267 28.641326-181.379458 27.791064-68.274516-0.862973-129.364283-11.040029-180.533878-31.80489-46.159002-18.731189-98.338744-46.827973-141.596418-87.541551-43.946046-41.361142-70.369064-75.958317-93.88139-127.198155-26.157437-57.004361-40.094111-129.065922-39.680686-191.781288 0-36.980719 4.033895-70.902234 12.252873-105.241856 8.532726-35.651474 20.069131-69.572989 38.13135-102.35257 18.856956-34.221214 36.754607-62.067803 58.869452-88.973149 23.248751-28.285434 39.2104-46.417894 64.295476-63.475987 18.297696-12.442861 36.879036-9.295353 47.199252-2.306612 4.403836 2.982273 8.919391 6.577992 12.933218 12.933217 9.572307 15.156208-0.334486 29.769212-6.69038 38.465836-7.148625 9.781026-23.130343 26.023643-38.738775 43.218205-38.192895 42.075603-55.133918 65.965228-74.986303 106.965794-30.772668 63.552249-37.495827 115.718611-38.131349 166.573791-0.668971 53.517684 9.995096 99.647251 27.427813 140.483919 33.916163 80.572211 94.807915 144.44289 175.270414 178.615938 41.108271 17.845472 113.812713 37.319888 181.960793 38.13135 56.193568 0.668971 125.919751-11.321666 166.574459-28.096784 45.935566-18.954626 97.223569-56.862539 127.10383-94.324918 23.013273-28.852721 52.179742-70.910931 64.413884-105.694749 14.863868-42.260239 24.806784-87.661297 24.559934-132.458943 0-54.414105-11.53373-108.417461-36.918505-156.856317-20.16747-38.483228-46.480777-74.607665-84.66899-108.048189-13.377414-11.714352-23.822728-20.067124-38.808348-31.619586-10.191774-7.857065-36.059546-25.027545-28.923632-47.326356 4.970455-15.53217 18.303717-25.294464 31.887843-27.205046 19.456354-2.736092 28.565733 2.427027 43.705885 12.041479l6.179955 4.322891zM510.755379 531.65738c-8.696624-0.668971-10.034566-0.446204-20.738102-6.689711-11.031333-6.434832-17.839451-21.183637-16.514219-35.175166V92.220334c0-18.178619 0.386665-22.815926 8.988295-31.685813 5.351768-5.519011 10.963097-11.381873 26.08987-11.539751 16.055305-0.167243 21.407073 3.846584 27.929542 9.700081 9.70677 8.711341 10.703537 17.56049 10.377078 33.525483v397.5715c-0.509756 15.273947 0.326458 22.967114-11.380535 33.502739-3.884046 3.495374-8.027653 7.693167-20.96087 8.362138l-3.791059 0.000669z m4.453341 0.573308"
p-id=
"2652"
fill=
"#ffffff"
></path></svg>
\ No newline at end of file
src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue
0 → 100644
View file @
12f52b28
<
template
>
<ContentWrap
:bodyStyle=
"
{ padding: '10px 20px 0' }">
<div
class=
"processInstance-wrap-main"
>
<el-scrollbar>
<div
class=
"text-#878c93 h-15px"
>
编号:
{{
selectProcessDefinition
.
id
}}
</div>
<el-divider
class=
"!my-8px"
/>
<div
class=
"flex items-center justify-between gap-5 mb-10px h-40px"
>
<div
class=
"text-26px font-bold mb-5px"
>
{{
selectProcessDefinition
.
name
}}
</div>
<el-button
style=
"float: right"
type=
"primary"
@
click=
"handleCancel"
>
<Icon
icon=
"ep:delete"
/>
选择其它流程
</el-button>
</div>
<!-- 中间主要内容tab栏 -->
<el-tabs
v-model=
"activeTab"
>
<!-- 表单信息 -->
<el-tab-pane
label=
"表单填写"
name=
"form"
>
<div
class=
"form-scroll-area"
>
<el-scrollbar>
<el-row>
<el-col
:span=
"17"
>
<form-create
:rule=
"detailForm.rule"
v-model:api=
"fApi"
v-model=
"detailForm.value"
:option=
"detailForm.option"
@
submit=
"submitForm"
>
<template
#
type-startUserSelect
>
<el-col
:span=
"24"
>
<el-card
class=
"mb-10px"
>
<template
#
header
>
指定审批人
</
template
>
<el-form
:model=
"startUserSelectAssignees"
:rules=
"startUserSelectAssigneesFormRules"
ref=
"startUserSelectAssigneesFormRef"
>
<el-form-item
v-for=
"userTask in startUserSelectTasks"
:key=
"userTask.id"
:label=
"`任务【${userTask.name}】`"
:prop=
"userTask.id"
>
<el-select
v-model=
"startUserSelectAssignees[userTask.id]"
multiple
placeholder=
"请选择审批人"
>
<el-option
v-for=
"user in userList"
:key=
"user.id"
:label=
"user.nickname"
:value=
"user.id"
/>
</el-select>
</el-form-item>
</el-form>
</el-card>
</el-col>
</template>
</form-create>
</el-col>
<el-col
:span=
"6"
:offset=
"1"
>
<!-- 流程时间线 -->
<ProcessInstanceTimeline
ref=
"timelineRef"
:approve-nodes=
"approveNodes"
:show-status-icon=
"false"
candidateField=
"candidateUserList"
/>
</el-col>
</el-row>
</el-scrollbar>
</div>
</el-tab-pane>
<!-- 流程图 -->
<el-tab-pane
label=
"流程图"
name=
"diagram"
>
<div
class=
"form-scroll-area"
>
<!-- 流程图预览 -->
<ProcessInstanceBpmnViewer
:bpmn-xml=
"bpmnXML"
/>
</div>
</el-tab-pane>
</el-tabs>
<!-- 底部操作栏 -->
<div
class=
"b-t-solid border-t-1px border-[var(--el-border-color)]"
>
<!-- 操作栏按钮 -->
<div
v-if=
"activeTab === 'form'"
class=
"h-50px bottom-10 text-14px flex items-center color-#32373c dark:color-#fff font-bold btn-container"
>
<el-button
plain
type=
"success"
@
click=
"submitForm"
>
<Icon
icon=
"ep:select"
/>
发起
</el-button>
<el-button
plain
type=
"danger"
@
click=
"handleCancel"
>
<Icon
icon=
"ep:close"
/>
取消
</el-button>
</div>
</div>
</el-scrollbar>
</div>
</ContentWrap>
</template>
<
script
lang=
"ts"
setup
>
import
{
setConfAndFields2
}
from
'@/utils/formCreate'
import
ProcessInstanceBpmnViewer
from
'../detail/ProcessInstanceBpmnViewer.vue'
import
ProcessInstanceTimeline
from
'../detail/ProcessInstanceTimeline.vue'
import
type
{
ApiAttrs
}
from
'@form-create/element-ui/types/config'
import
{
useTagsViewStore
}
from
'@/store/modules/tagsView'
import
*
as
ProcessInstanceApi
from
'@/api/bpm/processInstance'
import
*
as
DefinitionApi
from
'@/api/bpm/definition'
import
*
as
UserApi
from
'@/api/system/user'
defineOptions
({
name
:
'ProcessDefinitionDetail'
})
const
props
=
defineProps
<
{
selectProcessDefinition
:
any
}
>
()
const
{
push
,
currentRoute
}
=
useRouter
()
// 路由
const
message
=
useMessage
()
// 消息弹窗
const
{
delView
}
=
useTagsViewStore
()
// 视图操作
const
detailForm
:
any
=
ref
({
rule
:
[],
option
:
{},
value
:
{}
})
// 流程表单详情
const
fApi
=
ref
<
ApiAttrs
>
()
// 指定审批人
const
startUserSelectAssigneesFormRef
=
ref
()
// 发起人选择审批人的表单 Ref
const
startUserSelectTasks
:
any
=
ref
([])
// 发起人需要选择审批人的用户任务列表
const
startUserSelectAssignees
=
ref
({})
// 发起人选择审批人的数据
const
startUserSelectAssigneesFormRules
=
ref
({})
// 发起人选择审批人的表单 Rules
const
userList
=
ref
<
any
[]
>
([])
// 用户列表
const
bpmnXML
:
any
=
ref
(
null
)
// BPMN 数据
/** 当前的Tab */
const
activeTab
=
ref
(
'form'
)
const
emit
=
defineEmits
([
'cancel'
])
// 审批节点信息
const
approveNodes
=
ref
<
ProcessInstanceApi
.
ApprovalNodeInfo
[]
>
([])
/** 设置表单信息、获取流程图数据 **/
const
initProcessInfo
=
async
(
row
,
formVariables
?)
=>
{
// 重置指定审批人
startUserSelectTasks
.
value
=
[]
startUserSelectAssignees
.
value
=
{}
startUserSelectAssigneesFormRules
.
value
=
{}
// 情况一:流程表单
if
(
row
.
formType
==
10
)
{
// 设置表单
setConfAndFields2
(
detailForm
,
row
.
formConf
,
row
.
formFields
,
formVariables
)
await
nextTick
()
fApi
.
value
?.
btn
.
show
(
false
)
// 隐藏提交按钮
// 获取流程审批信息
getApprovalDetail
(
row
)
// 加载流程图
const
processDefinitionDetail
=
await
DefinitionApi
.
getProcessDefinition
(
row
.
id
)
if
(
processDefinitionDetail
)
{
bpmnXML
.
value
=
processDefinitionDetail
.
bpmnXml
startUserSelectTasks
.
value
=
processDefinitionDetail
.
startUserSelectTasks
// 设置指定审批人
if
(
startUserSelectTasks
.
value
?.
length
>
0
)
{
detailForm
.
value
.
rule
.
push
({
type
:
'startUserSelect'
,
props
:
{
title
:
'指定审批人'
}
})
// 设置校验规则
for
(
const
userTask
of
startUserSelectTasks
.
value
)
{
startUserSelectAssignees
.
value
[
userTask
.
id
]
=
[]
startUserSelectAssigneesFormRules
.
value
[
userTask
.
id
]
=
[
{
required
:
true
,
message
:
'请选择审批人'
,
trigger
:
'blur'
}
]
}
// 加载用户列表
userList
.
value
=
await
UserApi
.
getSimpleUserList
()
}
}
// 情况二:业务表单
}
else
if
(
row
.
formCustomCreatePath
)
{
await
push
({
path
:
row
.
formCustomCreatePath
})
// 这里暂时无需加载流程图,因为跳出到另外个 Tab;
}
}
/** 获取审批详情 */
const
getApprovalDetail
=
async
(
row
)
=>
{
try
{
const
param
=
{
processDefinitionId
:
row
.
id
}
const
data
=
await
ProcessInstanceApi
.
getApprovalDetail
(
param
)
if
(
!
data
)
{
message
.
error
(
'查询不到审批详情信息!'
)
return
}
// 获取审批节点,显示 Timeline 的数据
approveNodes
.
value
=
data
.
approveNodes
}
finally
{
}
}
/** 提交按钮 */
const
submitForm
=
async
(
formData
)
=>
{
if
(
!
fApi
.
value
||
props
.
selectProcessDefinition
)
{
return
}
// 如果有指定审批人,需要校验
if
(
startUserSelectTasks
.
value
?.
length
>
0
)
{
await
startUserSelectAssigneesFormRef
.
value
.
validate
()
}
// 提交请求
fApi
.
value
.
btn
.
loading
(
true
)
try
{
await
ProcessInstanceApi
.
createProcessInstance
({
processDefinitionId
:
props
.
selectProcessDefinition
.
id
,
variables
:
formData
||
detailForm
.
value
.
value
,
startUserSelectAssignees
:
startUserSelectAssignees
.
value
})
// 提示
message
.
success
(
'发起流程成功'
)
// 跳转回去
delView
(
unref
(
currentRoute
))
await
push
({
name
:
'BpmProcessInstanceMy'
})
}
finally
{
fApi
.
value
.
btn
.
loading
(
false
)
}
}
const
handleCancel
=
()
=>
{
emit
(
'cancel'
)
}
defineExpose
({
initProcessInfo
})
</
script
>
<
style
lang=
"scss"
scoped
>
$
wrap-padding-height
:
20px
;
$
wrap-margin-height
:
15px
;
$
button-height
:
51px
;
$
process-header-height
:
194px
;
.processInstance-wrap-main
{
height
:
calc
(
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
35px
);
max-height
:
calc
(
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
35px
);
overflow
:
auto
;
.form-scroll-area
{
height
:
calc
(
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
35px
-
$
process-header-height
-
40px
);
max-height
:
calc
(
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
35px
-
$
process-header-height
-
40px
);
overflow
:
auto
;
}
}
.form-box
{
:deep(.el-card)
{
border
:
none
;
}
}
</
style
>
src/views/bpm/processInstance/create/index_new.vue
0 → 100644
View file @
12f52b28
<
template
>
<!-- 第一步,通过流程定义的列表,选择对应的流程 -->
<ContentWrap
class=
"process-definition-container position-relative pb-20px"
v-if=
"!selectProcessDefinition"
v-loading=
"loading"
>
<el-row
:gutter=
"20"
class=
"!flex-nowrap"
>
<el-col
:span=
"5"
>
<div
class=
"flex flex-col"
>
<div
v-for=
"category in categoryList"
:key=
"category.code"
class=
"flex items-center p-10px cursor-pointer text-14px rounded-md"
:class=
"categoryActive.code === category.code ? 'text-#3e7bff bg-#e8eeff' : ''"
@
click=
"handleCategoryClick(category)"
>
{{
category
.
name
}}
</div>
</div>
</el-col>
<el-col
:span=
"19"
>
<h3
class=
"text-16px font-bold mb-10px mt-5px"
>
{{
categoryActive
.
name
}}
</h3>
<div
class=
"grid grid-cols-3 gap3"
v-if=
"categoryProcessDefinitionList.length"
>
<el-card
v-for=
"definition in categoryProcessDefinitionList"
:key=
"definition.id"
shadow=
"hover"
class=
"cursor-pointer definition-item-card"
@
click=
"handleSelect(definition)"
>
<template
#
default
>
<div
class=
"flex"
>
<el-image
:src=
"definition.icon"
class=
"w-32px h-32px"
/>
<el-text
class=
"!ml-10px"
size=
"large"
>
{{
definition
.
name
}}
</el-text>
</div>
</
template
>
</el-card>
</div>
<el-empty
v-else
/>
</el-col>
</el-row>
</ContentWrap>
<!-- 第二步,填写表单,进行流程的提交 -->
<ProcessDefinitionDetail
v-else
ref=
"processDefinitionDetailRef"
:selectProcessDefinition=
"selectProcessDefinition"
@
cancel=
"selectProcessDefinition = undefined"
/>
</template>
<
script
lang=
"ts"
setup
>
import
*
as
DefinitionApi
from
'@/api/bpm/definition'
import
*
as
ProcessInstanceApi
from
'@/api/bpm/processInstance'
import
{
CategoryApi
}
from
'@/api/bpm/category'
import
ProcessDefinitionDetail
from
'./ProcessDefinitionDetail.vue'
defineOptions
({
name
:
'BpmProcessInstanceCreate'
})
const
route
=
useRoute
()
// 路由
const
message
=
useMessage
()
// 消息
const
processInstanceId
:
any
=
route
.
query
.
processInstanceId
const
loading
=
ref
(
true
)
// 加载中
const
categoryList
:
any
=
ref
([])
// 分类的列表
const
categoryActive
:
any
=
ref
({})
// 选中的分类
const
processDefinitionList
=
ref
([])
// 流程定义的列表
/** 查询列表 */
const
getList
=
async
()
=>
{
loading
.
value
=
true
try
{
// 流程分类
categoryList
.
value
=
await
CategoryApi
.
getCategorySimpleList
()
if
(
categoryList
.
value
.
length
>
0
)
{
categoryActive
.
value
=
categoryList
.
value
[
0
]
}
// 流程定义
processDefinitionList
.
value
=
await
DefinitionApi
.
getProcessDefinitionList
({
suspensionState
:
1
})
// 如果 processInstanceId 非空,说明是重新发起
if
(
processInstanceId
?.
length
>
0
)
{
const
processInstance
=
await
ProcessInstanceApi
.
getProcessInstance
(
processInstanceId
)
if
(
!
processInstance
)
{
message
.
error
(
'重新发起流程失败,原因:流程实例不存在'
)
return
}
const
processDefinition
=
processDefinitionList
.
value
.
find
(
(
item
:
any
)
=>
item
.
key
==
processInstance
.
processDefinition
?.
key
)
if
(
!
processDefinition
)
{
message
.
error
(
'重新发起流程失败,原因:流程定义不存在'
)
return
}
await
handleSelect
(
processDefinition
,
processInstance
.
formVariables
)
}
}
finally
{
loading
.
value
=
false
}
}
/** 选中分类对应的流程定义列表 */
const
categoryProcessDefinitionList
:
any
=
computed
(()
=>
{
return
processDefinitionList
.
value
.
filter
(
(
item
:
any
)
=>
item
.
category
==
categoryActive
.
value
.
code
)
})
// ========== 表单相关 ==========
const
selectProcessDefinition
=
ref
()
// 选择的流程定义
const
processDefinitionDetailRef
=
ref
()
/** 处理选择流程的按钮操作 **/
const
handleSelect
=
async
(
row
,
formVariables
?)
=>
{
// 设置选择的流程
selectProcessDefinition
.
value
=
row
// 初始化流程定义详情
await
nextTick
()
processDefinitionDetailRef
.
value
?.
initProcessInfo
(
row
,
formVariables
)
}
// 左侧分类切换
const
handleCategoryClick
=
(
val
)
=>
{
categoryActive
.
value
=
val
}
/** 初始化 */
onMounted
(()
=>
{
getList
()
})
</
script
>
<
style
lang=
"scss"
scoped
>
.process-definition-container
::before
{
content
:
''
;
border-left
:
1px
solid
#e6e6e6
;
position
:
absolute
;
left
:
20.8%
;
height
:
100%
;
}
:deep
()
{
.definition-item-card
{
.el-card__body
{
padding
:
14px
;
}
}
}
</
style
>
src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue
View file @
12f52b28
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
>
>
<img
class=
"w-full h-full"
:src=
"getApprovalNodeImg(activity.nodeType)"
alt=
""
/>
<img
class=
"w-full h-full"
:src=
"getApprovalNodeImg(activity.nodeType)"
alt=
""
/>
<div
<div
v-if=
"showStatusIcon"
class=
"position-absolute top-17px left-17px bg-#fff rounded-full flex items-center p-2px"
class=
"position-absolute top-17px left-17px bg-#fff rounded-full flex items-center p-2px"
>
>
<el-icon
:size=
"12"
:color=
"getApprovalNodeColor(activity.status)"
>
<el-icon
:size=
"12"
:color=
"getApprovalNodeColor(activity.status)"
>
...
@@ -131,11 +132,18 @@ import auditorSvg from '@/assets/svgs/bpm/auditor.svg'
...
@@ -131,11 +132,18 @@ import auditorSvg from '@/assets/svgs/bpm/auditor.svg'
import
copySvg
from
'@/assets/svgs/bpm/copy.svg'
import
copySvg
from
'@/assets/svgs/bpm/copy.svg'
import
conditionSvg
from
'@/assets/svgs/bpm/condition.svg'
import
conditionSvg
from
'@/assets/svgs/bpm/condition.svg'
import
parallelSvg
from
'@/assets/svgs/bpm/parallel.svg'
import
parallelSvg
from
'@/assets/svgs/bpm/parallel.svg'
import
finishSvg
from
'@/assets/svgs/bpm/finish.svg'
defineOptions
({
name
:
'BpmProcessInstanceTimeline'
})
defineOptions
({
name
:
'BpmProcessInstanceTimeline'
})
defineProps
<
{
withDefaults
(
defineProps
<
{
approveNodes
:
ProcessInstanceApi
.
ApprovalNodeInfo
[]
// 审批节点信息
approveNodes
:
ProcessInstanceApi
.
ApprovalNodeInfo
[]
// 审批节点信息
}
>
()
showStatusIcon
?:
boolean
// 是否显示头像右下角状态图标
}
>
(),
{
showStatusIcon
:
true
// 默认值为 true
}
)
// 审批节点
// 审批节点
const
statusIconMap2
=
{
const
statusIconMap2
=
{
...
@@ -180,6 +188,8 @@ const statusIconMap = {
...
@@ -180,6 +188,8 @@ const statusIconMap = {
}
}
const
nodeTypeSvgMap
=
{
const
nodeTypeSvgMap
=
{
// 结束节点
[
NodeType
.
END_EVENT_NODE
]:
{
color
:
'#ffffff'
,
svg
:
finishSvg
},
// 发起人节点
// 发起人节点
[
NodeType
.
START_USER_NODE
]:
{
color
:
'#ffffff'
,
svg
:
starterSvg
},
[
NodeType
.
START_USER_NODE
]:
{
color
:
'#ffffff'
,
svg
:
starterSvg
},
// 审批人节点
// 审批人节点
...
...
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