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
538ad86b
authored
Jul 13, 2024
by
jason
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
仿钉钉流程设计器- 操作按钮设置
parent
fae712b9
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
311 additions
and
103 deletions
+311
-103
src/components/SimpleProcessDesignerV2/src/NodeHandler.vue
+0
-22
src/components/SimpleProcessDesignerV2/src/consts.ts
+54
-0
src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue
+137
-4
src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss
+52
-59
src/views/bpm/processInstance/detail/index.vue
+68
-18
No files found.
src/components/SimpleProcessDesignerV2/src/NodeHandler.vue
View file @
538ad86b
...
...
@@ -81,8 +81,6 @@ const addNode = (type: number) => {
type
:
NodeType
.
USER_TASK_NODE
,
approveMethod
:
ApproveMethodType
.
RRANDOM_SELECT_ONE_APPROVE
,
candidateStrategy
:
CandidateStrategy
.
USER
,
// candidateParam: undefined,
// fieldsPermission: undefined,
// 超时处理
rejectHandler
:
{
type
:
RejectHandlerType
.
FINISH_PROCESS
...
...
@@ -92,20 +90,6 @@ const addNode = (type: number) => {
},
childNode
:
props
.
childNode
// 审批节点配置
// attributes: {
// approveMethod: ApproveMethodType.RRANDOM_SELECT_ONE_APPROVE,
// candidateStrategy: CandidateStrategy.USER,
// candidateParam: undefined,
// fieldsPermission: undefined,
// // 超时处理
// timeoutHandler: {
// enable: false
// },
// rejectHandler: {
// type: RejectHandlerType.FINISH_PROCESS
// }
// },
}
emits
(
'update:childNode'
,
data
)
}
...
...
@@ -118,12 +102,6 @@ const addNode = (type: number) => {
candidateStrategy
:
CandidateStrategy
.
USER
,
candidateParam
:
undefined
,
fieldsPermission
:
undefined
,
// 审批节点配置
// attributes: {
// candidateStrategy: CandidateStrategy.USER,
// candidateParam: undefined,
// fieldsPermission: undefined
// },
childNode
:
props
.
childNode
}
emits
(
'update:childNode'
,
data
)
...
...
src/components/SimpleProcessDesignerV2/src/consts.ts
View file @
538ad86b
...
...
@@ -182,6 +182,8 @@ export type SimpleFlowNode = {
approveMethod
?:
ApproveMethodType
//通过比例
approveRatio
?:
number
// 审批按钮设置
buttonsSetting
?:
any
[]
// 表单权限
fieldsPermission
?:
any
[]
// 审批任务超时处理
...
...
@@ -214,6 +216,40 @@ export type ConditionRule = {
rightSide
:
string
}
// 审批操作按钮类型
export
enum
OpsButtonType
{
/**
* 通过
*/
APPROVE
=
1
,
/**
* 拒绝
*/
REJECT
=
2
,
/**
* 转办
*/
TRANSFER
=
3
,
/**
* 委派
*/
DELEGATE
=
4
,
/**
* 加签
*/
ADD_SIGN
=
5
,
/**
* 回退
*/
RETURN
=
6
}
export
type
ButtonSetting
=
{
id
:
OpsButtonType
displayName
:
string
enable
:
boolean
}
export
const
NODE_DEFAULT_TEXT
=
new
Map
<
number
,
string
>
()
NODE_DEFAULT_TEXT
.
set
(
NodeType
.
USER_TASK_NODE
,
'请配置审批人'
)
NODE_DEFAULT_TEXT
.
set
(
NodeType
.
COPY_TASK_NODE
,
'请配置抄送人'
)
...
...
@@ -281,3 +317,21 @@ export const COMPARISON_OPERATORS: DictDataVO = [
label
:
'小于等于'
}
]
// 审批操作按钮名称
export
const
OPERATION_BUTTON_NAME
=
new
Map
<
number
,
string
>
()
OPERATION_BUTTON_NAME
.
set
(
OpsButtonType
.
APPROVE
,
'通过'
)
OPERATION_BUTTON_NAME
.
set
(
OpsButtonType
.
REJECT
,
'拒绝'
)
OPERATION_BUTTON_NAME
.
set
(
OpsButtonType
.
TRANSFER
,
'转办'
)
OPERATION_BUTTON_NAME
.
set
(
OpsButtonType
.
DELEGATE
,
'委派'
)
OPERATION_BUTTON_NAME
.
set
(
OpsButtonType
.
ADD_SIGN
,
'加签'
)
OPERATION_BUTTON_NAME
.
set
(
OpsButtonType
.
RETURN
,
'回退'
)
// 默认的按钮权限设置
export
const
DEFAULT_BUTTON_SETTING
:
ButtonSetting
[]
=
[
{
id
:
OpsButtonType
.
APPROVE
,
displayName
:
'通过'
,
enable
:
true
},
{
id
:
OpsButtonType
.
REJECT
,
displayName
:
'拒绝'
,
enable
:
true
},
{
id
:
OpsButtonType
.
TRANSFER
,
displayName
:
'转办'
,
enable
:
false
},
{
id
:
OpsButtonType
.
DELEGATE
,
displayName
:
'委派'
,
enable
:
false
},
{
id
:
OpsButtonType
.
ADD_SIGN
,
displayName
:
'加签'
,
enable
:
false
},
{
id
:
OpsButtonType
.
RETURN
,
displayName
:
'回退'
,
enable
:
false
}
]
src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue
View file @
538ad86b
...
...
@@ -280,6 +280,40 @@
</el-form>
</div>
</el-tab-pane>
<el-tab-pane
label=
"操作按钮设置"
name=
"buttons"
v-if=
"formType === 10"
>
<div
class=
"button-setting-pane"
>
<div
class=
"button-setting-desc"
>
操作按钮
</div>
<div
class=
"button-setting-title"
>
<div
class=
"button-title-label"
>
操作按钮
</div>
<div
class=
"pl-4 button-title-label"
>
显示名称
</div>
<div
class=
"button-title-label"
>
启用
</div>
</div>
<div
class=
"button-setting-item"
v-for=
"(item, index) in configForm.buttonsSetting"
:key=
"index"
>
<div
class=
"button-setting-item-label"
>
{{ OPERATION_BUTTON_NAME.get(item.id) }}
</div>
<div
class=
"button-setting-item-label"
>
<input
type=
"text"
class=
"editable-title-input"
@
blur=
"btnDisplayNameBlurEvent(index)"
v-mountedFocus
v-model=
"item.displayName"
:placeholder=
"item.displayName"
v-if=
"btnDisplayNameEdit[index]"
/>
<el-button
v-else
text
@
click=
"changeBtnDisplayName(index)"
>
{{ item.displayName }}
<Icon
icon=
"ep:edit"
/></el-button>
</div>
<div
class=
"button-setting-item-label"
>
<el-switch
v-model=
"item.enable"
/>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane
label=
"表单字段权限"
name=
"fields"
v-if=
"formType === 10"
>
<div
class=
"field-setting-pane"
>
<div
class=
"field-setting-desc"
>
字段权限
</div>
...
...
@@ -334,7 +368,9 @@ import {
TIMEOUT_HANDLER_ACTION_TYPES
,
TIME_UNIT_TYPES
,
REJECT_HANDLER_TYPES
,
NODE_DEFAULT_NAME
NODE_DEFAULT_NAME
,
DEFAULT_BUTTON_SETTING
,
OPERATION_BUTTON_NAME
}
from
'../consts'
import
{
DICT_TYPE
,
getIntDictOptions
}
from
'@/utils/dict'
import
{
getDefaultFieldsPermission
}
from
'../utils'
...
...
@@ -345,7 +381,6 @@ import * as PostApi from '@/api/system/post'
import
*
as
UserApi
from
'@/api/system/user'
import
*
as
UserGroupApi
from
'@/api/bpm/userGroup'
import
{
cloneDeep
}
from
'lodash-es'
defineOptions
({
name
:
'UserTaskNodeConfig'
})
...
...
@@ -385,7 +420,8 @@ const configForm = ref<any>({
timeoutHandlerAction
:
1
,
timeDuration
:
6
,
// 默认 6小时
maxRemindCount
:
1
,
// 默认 提醒 1次
fieldsPermission
:
[]
fieldsPermission
:
[],
buttonsSetting
:
[]
})
// 表单校验规则
const
formRules
=
reactive
({
...
...
@@ -432,6 +468,8 @@ const saveConfig = async () => {
}
// 设置表单权限
currentNode
.
value
.
fieldsPermission
=
configForm
.
value
.
fieldsPermission
// 设置按钮权限
currentNode
.
value
.
buttonsSetting
=
configForm
.
value
.
buttonsSetting
currentNode
.
value
.
showText
=
getShowText
()
settingVisible
.
value
=
false
...
...
@@ -533,6 +571,7 @@ const setCurrentNode = (node: SimpleFlowNode) => {
currentNode
.
value
=
node
configForm
.
value
.
fieldsPermission
=
cloneDeep
(
node
.
fieldsPermission
)
||
getDefaultFieldsPermission
(
formFields
?.
value
)
configForm
.
value
.
buttonsSetting
=
cloneDeep
(
node
.
buttonsSetting
)
||
DEFAULT_BUTTON_SETTING
configForm
.
value
.
candidateStrategy
=
node
.
candidateStrategy
const
strCandidateParam
=
node
?.
candidateParam
if
(
node
.
candidateStrategy
===
CandidateStrategy
.
EXPRESSION
)
{
...
...
@@ -614,6 +653,7 @@ const blurEvent = () => {
currentNode
.
value
.
name
=
currentNode
.
value
.
name
||
(
NODE_DEFAULT_NAME
.
get
(
NodeType
.
USER_TASK_NODE
)
as
string
)
}
const
approveMethodChanged
=
()
=>
{
configForm
.
value
.
rejectHandlerType
=
RejectHandlerType
.
FINISH_PROCESS
if
(
configForm
.
value
.
approveMethod
===
ApproveMethodType
.
APPROVE_BY_RATIO
)
{
...
...
@@ -703,6 +743,99 @@ const convertTimeUnit = (strTimeUnit: string) => {
}
return
TimeUnitType
.
HOUR
}
// 操作按钮显示名称可编辑
const
btnDisplayNameEdit
=
ref
<
boolean
[]
>
([])
const
changeBtnDisplayName
=
(
index
:
number
)
=>
{
btnDisplayNameEdit
.
value
[
index
]
=
true
}
const
btnDisplayNameBlurEvent
=
(
index
:
number
)
=>
{
btnDisplayNameEdit
.
value
[
index
]
=
false
const
buttonItem
=
configForm
.
value
.
buttonPermission
[
index
]
buttonItem
.
displayName
=
buttonItem
.
displayName
||
OPERATION_BUTTON_NAME
.
get
(
buttonItem
.
id
)
}
</
script
>
<
style
lang=
"scss"
scoped
></
style
>
<
style
lang=
"scss"
scoped
>
.button-setting-pane
{
display
:
flex
;
flex-direction
:
column
;
font-size
:
14px
;
.button-setting-desc
{
padding-right
:
8px
;
margin-bottom
:
16px
;
font-size
:
16px
;
font-weight
:
700
;
}
.button-setting-title
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
height
:
45px
;
padding-left
:
12px
;
background-color
:
#f8fafc0
a
;
border
:
1px
solid
#1f38581
a
;
&
>
:first-child
{
width
:
100px
!important
;
text-align
:
left
!important
;
}
&
>
:last-child
{
text-align
:
center
!important
;
}
.button-title-label
{
width
:
150px
;
font-size
:
13px
;
font-weight
:
700
;
color
:
#000
;
text-align
:
left
;
}
}
.button-setting-item
{
align-items
:
center
;
display
:
flex
;
justify-content
:
space-between
;
height
:
38px
;
padding-left
:
12px
;
border
:
1px
solid
#1f38581
a
;
border-top
:
0
;
&
>
:first-child
{
width
:
100px
!important
;
}
&
>
:last-child
{
text-align
:
center
!important
;
}
.button-setting-item-label
{
width
:
150px
;
overflow
:
hidden
;
text-align
:
left
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
.editable-title-input
{
height
:
24px
;
max-width
:
130px
;
margin-left
:
4px
;
line-height
:
24px
;
border
:
1px
solid
#d9d9d9
;
border-radius
:
4px
;
transition
:
all
0.3s
;
&:focus
{
border-color
:
#40a9ff
;
outline
:
0
;
box-shadow
:
0
0
0
2px
rgb
(
24
144
255
/
20%
);
}
}
}
}
</
style
>
src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss
View file @
538ad86b
...
...
@@ -241,7 +241,7 @@
.move-node-left
{
left
:
-2px
;
top
:
0px
;
background
:
rgba
(
126
,
134
,
142
,
.08
);
background
:
rgba
(
126
,
134
,
142
,
0
.08
);
border-top-left-radius
:
8px
;
border-bottom-left-radius
:
8px
;
}
...
...
@@ -249,13 +249,12 @@
.move-node-right
{
right
:
-2px
;
top
:
0px
;
background
:
rgba
(
126
,
134
,
142
,
.
08
);
background
:
rgba
(
126
,
134
,
142
,
0
.08
);
border-top-right-radius
:
6px
;
border-bottom-right-radius
:
6px
;
}
}
.node-config-error
{
border-color
:
#ff5219
!
important
;
}
...
...
@@ -306,12 +305,10 @@
background-color
:
#0089ff
;
border-radius
:
50%
;
&
:hover
{
&
:hover
{
transform
:
scale
(
1
.1
);
}
}
}
.node-handler-arrow
{
...
...
@@ -323,7 +320,6 @@
}
}
// 条件节点包装
.branch-node-wrapper
{
position
:
relative
;
...
...
@@ -341,8 +337,8 @@
position
:
absolute
;
height
:
100%
;
width
:
4px
;
background-color
:
#fafafa
;
content
:
""
;
background-color
:
#fafafa
;
content
:
''
;
left
:
50%
;
transform
:
translate
(
-50%
);
}
...
...
@@ -498,10 +494,10 @@
border-radius
:
4px
;
transition
:
all
0
.3s
;
&
:focus
{
&
:focus
{
border-color
:
#40a9ff
;
outline
:
0
;
box-shadow
:
0
0
0
2px
rgba
(
24
,
144
,
255
,
.2
)
box-shadow
:
0
0
0
2px
rgba
(
24
,
144
,
255
,
0
.2
);
}
}
}
...
...
@@ -537,11 +533,11 @@
border
:
1px
solid
#d9d9d9
;
border-radius
:
4px
;
transition
:
all
0
.3s
;
&
:focus
{
&
:focus
{
border-color
:
#40a9ff
;
outline
:
0
;
box-shadow
:
0
0
0
2px
rgba
(
24
,
144
,
255
,
.2
)
box-shadow
:
0
0
0
2px
rgba
(
24
,
144
,
255
,
0
.2
);
}
}
}
...
...
@@ -550,7 +546,6 @@
.field-setting-pane
{
display
:
flex
;
flex-direction
:
column
;
padding
:
16px
0
;
font-size
:
14px
;
.field-setting-desc
{
...
...
@@ -569,7 +564,7 @@
line-height
:
45px
;
background-color
:
#f8fafc
0a
;
border
:
1px
solid
#1f3858
1a
;
.first-title
{
text-align
:
left
!
important
;
}
...
...
@@ -588,9 +583,8 @@
color
:
#000
;
text-align
:
center
;
}
}
.field-setting-item
{
align-items
:
center
;
display
:
flex
;
...
...
@@ -632,56 +626,56 @@
margin-right
:
8px
;
}
.handler-item-icon
{
width
:
80px
;
height
:
80px
;
background
:
#fff
;
border
:
1px
solid
#e2e2e2
;
border-radius
:
50%
;
transition
:
all
0
.3s
cubic-bezier
(
0
.645
,
0
.045
,
0
.355
,
1
);
user-select
:
none
;
text-align
:
center
;
&
:hover
{
background
:
#e2e2e2
;
box-shadow
:
0
2px
4px
0
rgba
(
0
,
0
,
0
,
0
.1
);
}
.icon-size
{
font-size
:
35px
;
line-height
:
80px
;
}
.handler-item-icon
{
width
:
80px
;
height
:
80px
;
background
:
#fff
;
border
:
1px
solid
#e2e2e2
;
border-radius
:
50%
;
transition
:
all
0
.3s
cubic-bezier
(
0
.645
,
0
.045
,
0
.355
,
1
);
user-select
:
none
;
text-align
:
center
;
&
:hover
{
background
:
#e2e2e2
;
box-shadow
:
0
2px
4px
0
rgba
(
0
,
0
,
0
,
0
.1
);
}
.icon-size
{
font-size
:
35px
;
line-height
:
80px
;
}
}
.approve
{
color
:
#ff943e
;
.approve
{
color
:
#ff943e
;
}
.copy
{
color
:
#3296fa
;
}
.condition
{
color
:
#15bc83
;
}
.handler-item-text
{
margin-top
:
4px
;
width
:
80px
;
text-align
:
center
;
}
.handler-item-text
{
margin-top
:
4px
;
width
:
80px
;
text-align
:
center
;
}
}
// iconfont 样式
@font-face
{
font-family
:
"iconfont"
;
/* Project id 4495938 */
src
:
url('iconfont.woff2?t=1712392083512')
format
(
'woff2'
)
,
url('iconfont.woff?t=1712392083512')
format
(
'woff'
)
,
url('iconfont.ttf?t=1712392083512')
format
(
'truetype'
);
font-family
:
'iconfont'
;
/* Project id 4495938 */
src
:
url('iconfont.woff2?t=1712392083512')
format
(
'woff2'
)
,
url('iconfont.woff?t=1712392083512')
format
(
'woff'
)
,
url('iconfont.ttf?t=1712392083512')
format
(
'truetype'
);
}
.iconfont
{
font-family
:
"iconfont"
!
important
;
font-family
:
'iconfont'
!
important
;
font-size
:
16px
;
font-style
:
normal
;
-webkit-font-smoothing
:
antialiased
;
...
...
@@ -689,25 +683,25 @@
}
.icon-Inclusive
:before
{
content
:
"\e602"
;
content
:
'\e602'
;
}
.icon-copy
:before
{
content
:
"\e7eb"
;
content
:
'\e7eb'
;
}
.icon-handle
:before
{
content
:
"\e61c"
;
content
:
'\e61c'
;
}
.icon-exclusive
:before
{
content
:
"\e717"
;
content
:
'\e717'
;
}
.icon-approve
:before
{
content
:
"\e715"
;
content
:
'\e715'
;
}
.icon-parallel
:before
{
content
:
"\e688"
;
}
\ No newline at end of file
content
:
'\e688'
;
}
src/views/bpm/processInstance/detail/index.vue
View file @
538ad86b
...
...
@@ -56,29 +56,71 @@
</el-form-item>
</el-form>
<div
style=
"margin-bottom: 20px; margin-left: 10%; font-size: 14px"
>
<el-button
type=
"success"
@
click=
"handleAudit(item, true)"
>
<el-button
type=
"success"
v-if=
"!item.buttonsSetting || item.buttonsSetting[OpsButtonType.APPROVE]?.enable"
@
click=
"handleAudit(item, true)"
>
<Icon
icon=
"ep:select"
/>
通过
{{
item.buttonsSetting?.[OpsButtonType.APPROVE]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.APPROVE)
}}
</el-button>
<el-button
type=
"danger"
@
click=
"handleAudit(item, false)"
>
<el-button
v-if=
"!item.buttonsSetting || item.buttonsSetting[OpsButtonType.REJECT]?.enable"
type=
"danger"
@
click=
"handleAudit(item, false)"
>
<Icon
icon=
"ep:close"
/>
不通过
{{
item.buttonsSetting?.[OpsButtonType.REJECT].displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.REJECT)
}}
</el-button>
<el-button
type=
"primary"
@
click=
"openTaskUpdateAssigneeForm(item.id)"
>
<el-button
v-if=
"!item.buttonsSetting || item.buttonsSetting[OpsButtonType.TRANSFER]?.enable"
type=
"primary"
@
click=
"openTaskUpdateAssigneeForm(item.id)"
>
<Icon
icon=
"ep:edit"
/>
转办
{{
item.buttonsSetting?.[OpsButtonType.TRANSFER]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.TRANSFER)
}}
</el-button>
<el-button
type=
"primary"
@
click=
"handleDelegate(item)"
>
<el-button
v-if=
"!item.buttonsSetting || item.buttonsSetting[OpsButtonType.DELEGATE]?.enable"
type=
"primary"
@
click=
"handleDelegate(item)"
>
<Icon
icon=
"ep:position"
/>
委派
{{
item.buttonsSetting?.[OpsButtonType.DELEGATE]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.DELEGATE)
}}
</el-button>
<el-button
type=
"primary"
@
click=
"handleSign(item)"
>
<el-button
v-if=
"!item.buttonsSetting || item.buttonsSetting[OpsButtonType.ADD_SIGN]?.enable"
type=
"primary"
@
click=
"handleSign(item)"
>
<Icon
icon=
"ep:plus"
/>
加签
{{
item.buttonsSetting?.[OpsButtonType.ADD_SIGN]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.ADD_SIGN)
}}
</el-button>
<el-button
type=
"warning"
@
click=
"handleBack(item)"
>
<el-button
v-if=
"!item.buttonsSetting || item.buttonsSetting[OpsButtonType.RETURN]?.enable"
type=
"warning"
@
click=
"handleBack(item)"
>
<Icon
icon=
"ep:back"
/>
回退
{{
item.buttonsSetting?.[OpsButtonType.RETURN]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.RETURN)
}}
</el-button>
</div>
</el-col>
...
...
@@ -147,6 +189,10 @@ import TaskSignCreateForm from './dialog/TaskSignCreateForm.vue'
import
{
registerComponent
}
from
'@/utils/routerHelper'
import
{
isEmpty
}
from
'@/utils/is'
import
*
as
UserApi
from
'@/api/system/user'
import
{
OpsButtonType
,
OPERATION_BUTTON_NAME
}
from
'@/components/SimpleProcessDesignerV2/src/consts'
defineOptions
({
name
:
'BpmProcessInstanceDetail'
})
...
...
@@ -280,12 +326,16 @@ const getProcessInstance = async () => {
// 设置表单信息
const
processDefinition
=
data
.
processDefinition
if
(
processDefinition
.
formType
===
10
)
{
setConfAndFields2
(
detailForm
,
processDefinition
.
formConf
,
processDefinition
.
formFields
,
data
.
formVariables
)
if
(
detailForm
.
value
.
rule
.
length
>
0
)
{
detailForm
.
value
.
value
=
data
.
formVariables
}
else
{
setConfAndFields2
(
detailForm
,
processDefinition
.
formConf
,
processDefinition
.
formFields
,
data
.
formVariables
)
}
nextTick
().
then
(()
=>
{
fApi
.
value
?.
btn
.
show
(
false
)
fApi
.
value
?.
resetBtn
.
show
(
false
)
...
...
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