Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
Z
zrch-risk-39
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
Administrator
zrch-risk-39
Commits
62b97b82
提交
62b97b82
authored
4月 07, 2026
作者:
liuluyu
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'master' of
http://47.97.51.208/root/zrch-risk-39
上级
54da6b43
2d318c87
隐藏空白字符变更
内嵌
并排
正在显示
30 个修改的文件
包含
1665 行增加
和
1994 行删除
+1665
-1994
todo.js
zrch-risk-client-39/src/components/Process/api/todo.js
+11
-0
ApprovalPanel.vue
zrch-risk-client-39/src/views/common/ApprovalPanel.vue
+230
-600
CurrentFormPanel.vue
zrch-risk-client-39/src/views/common/CurrentFormPanel.vue
+53
-41
FlowHistoryChart.vue
zrch-risk-client-39/src/views/common/FlowHistoryChart.vue
+96
-0
FlowHistoryDrawer.vue
zrch-risk-client-39/src/views/common/FlowHistoryDrawer.vue
+32
-0
FlowHistoryRecord.vue
zrch-risk-client-39/src/views/common/FlowHistoryRecord.vue
+244
-0
HistoryPanel.vue
zrch-risk-client-39/src/views/common/HistoryPanel.vue
+100
-77
TaskAssigneeDrawer.vue
zrch-risk-client-39/src/views/common/TaskAssigneeDrawer.vue
+227
-441
TaskAssigneeSelector.vue
...-risk-client-39/src/views/common/TaskAssigneeSelector.vue
+0
-420
WorkFlowFormDrawer.vue
zrch-risk-client-39/src/views/common/WorkFlowFormDrawer.vue
+122
-149
Detail.vue
...9/src/views/flowable/task/myProcess/components/Detail.vue
+1
-1
Send.vue
...-39/src/views/flowable/task/myProcess/components/Send.vue
+1
-0
StProblemCheckArchiveList.vue
...iews/project/problemArchive/StProblemCheckArchiveList.vue
+27
-71
StProblemArchiveList.vue
...9/src/views/project/problemCheck/StProblemArchiveList.vue
+2
-2
StProblemCheck.data.ts
...-39/src/views/project/problemCheck/StProblemCheck.data.ts
+2
-3
StProblemCheckList.vue
...-39/src/views/project/problemCheck/StProblemCheckList.vue
+14
-1
StProblemExecApprovalList.vue
.../views/project/problemCheck/StProblemExecApprovalList.vue
+2
-2
StProblemExecList.vue
...t-39/src/views/project/problemCheck/StProblemExecList.vue
+2
-2
StProblemIndex.vue
...ient-39/src/views/project/problemCheck/StProblemIndex.vue
+94
-128
StProblemPlanApprovalList.vue
.../views/project/problemCheck/StProblemPlanApprovalList.vue
+2
-2
StProblemPlanList.vue
...t-39/src/views/project/problemCheck/StProblemPlanList.vue
+2
-3
StProblemCheckExecuteForm.vue
...ect/problemCheck/components/StProblemCheckExecuteForm.vue
+13
-2
StProblemCheckForm.vue
...ws/project/problemCheck/components/StProblemCheckForm.vue
+1
-1
StProblemCheckPlanForm.vue
...roject/problemCheck/components/StProblemCheckPlanForm.vue
+97
-21
FlowFormController.java
...jeecg/modules/flowable/controller/FlowFormController.java
+109
-5
IFlowRejectService.java
...rg/jeecg/modules/flowable/service/IFlowRejectService.java
+19
-0
FlowDefinitionServiceImpl.java
...ules/flowable/service/impl/FlowDefinitionServiceImpl.java
+0
-12
FlowRejectServiceImpl.java
.../modules/flowable/service/impl/FlowRejectServiceImpl.java
+131
-0
StProblemCheck.java
.../org/jeecg/modules/stm/problem/entity/StProblemCheck.java
+21
-0
application-dev.yml
...jeecg-system-start/src/main/resources/application-dev.yml
+10
-10
没有找到文件。
zrch-risk-client-39/src/components/Process/api/todo.js
浏览文件 @
62b97b82
...
...
@@ -147,6 +147,17 @@ export function getMyTaskFlow(data) {
})
}
// 得到退回的节点
export
function
getRejectNode
(
query
)
{
alert
(
JSON
.
stringify
(
query
))
return
defHttp
.
get
({
url
:
'/flowable/form/getRejectTargetNode'
,
params
:
query
})
}
zrch-risk-client-39/src/views/common/ApprovalPanel.vue
浏览文件 @
62b97b82
<
template
>
<div
class=
"approval-panel"
>
<div
class=
"approval-header"
>
<span
class=
"approval-title"
>
{{
title
}}
</span>
<a-tag
:color=
"tagColor"
v-if=
"showTag"
>
{{
tagText
}}
</a-tag>
</div>
<div
class=
"approval-content"
>
<a-form
:model=
"approvalForm"
layout=
"vertical"
>
<a-form-item
label=
"审核结果"
required
>
<a-radio-group
v-model:value=
"approvalForm.result"
class=
"result-group"
>
<a-radio
v-for=
"option in resultOptions"
:key=
"option.value"
:value=
"option.value"
:class=
"option.className"
>
<div
class=
"radio-content"
>
<component
:is=
"option.icon"
class=
"radio-icon"
/>
<span
class=
"radio-label"
>
{{
option
.
label
}}
</span>
</div>
</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
:label=
"commentLabel"
:required=
"isCommentRequired"
:extra=
"commentExtra"
>
<a-textarea
v-model:value=
"approvalForm.comment"
:rows=
"commentRows"
:placeholder=
"commentPlaceholder"
:maxlength=
"commentMaxLength"
show-count
/>
</a-form-item>
<!-- 退回节点选择(仅当支持退回且结果选择退回时显示) -->
<a-form-item
v-if=
"showRejectNodeSelect && approvalForm.result === 'rejected'"
label=
"退回节点"
required
>
<a-select
v-model:value=
"approvalForm.rejectNode"
placeholder=
"请选择退回节点"
:options=
"rejectNodeOptions"
:disabled=
"!canSelectRejectNode"
>
<template
v-if=
"rejectNodeOptions.length === 0"
>
<a-select-option
value=
""
disabled
>
无可退回的节点
</a-select-option>
</
template
>
</a-select>
<div
class=
"form-tip"
v-if=
"rejectNodeOptions.length === 0"
>
{{ emptyRejectNodesTip }}
</div>
</a-form-item>
<!-- 自定义扩展区域 -->
<slot
name=
"extra-fields"
></slot>
<!-- 附加说明 -->
<div
v-if=
"showAdditionalInfo && approvalForm.result === 'rejected'"
class=
"additional-info"
>
<div
class=
"info-tip"
>
<info-circle-outlined
/>
<span>
{{ additionalInfoText }}
</span>
</div>
<a-card
class=
"approval-card"
:bordered=
"false"
>
<template
#
title
>
<div
class=
"approval-header"
>
<span
class=
"approval-title"
>
审批操作
</span>
</div>
<a-divider
v-if=
"showSummary"
/>
<div
class=
"approval-summary"
v-if=
"showSummary"
>
<div
v-for=
"item in summaryItems"
:key=
"item.label"
class=
"summary-item"
>
<span
class=
"summary-label"
>
{{ item.label }}
</span>
<span
class=
"summary-value"
>
{{ item.value || '--' }}
</span>
</div>
</div>
</a-form>
<a-space
direction=
""
style=
"width:100%"
>
<a-button
type=
"primary"
ghost
block
@
click=
"handleSubmit"
>
保存
</a-button>
<a-button
type=
"primary"
ghost
block
@
click=
"handleSubmit"
>
保存并发送
</a-button>
</a-space>
</div>
</
template
>
<
template
#
extra
>
<a-button
@
click=
"handleSaveApp"
type=
"primary"
size=
"small"
>
保存审批
</a-button>
</
template
>
<div
class=
"approval-content"
>
<BasicForm
@
register=
"registerForm"
style=
"width: 100%;"
>
<
template
#
slot_passOrReturn=
"{ model }"
>
<a-radio-group
v-model:value=
"model.reviewStatus"
button-style=
"solid"
class=
"approval-radio-group"
>
<a-radio-button
:value=
"true"
class=
"radio-button"
>
<CheckCircleOutlined
/>
通过
</a-radio-button>
<a-radio-button
:value=
"false"
class=
"radio-button"
>
<CloseCircleOutlined
/>
退回
</a-radio-button>
</a-radio-group>
</
template
>
</BasicForm>
</div>
</a-card>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
{
ref
,
computed
,
watch
,
onUnmounted
}
from
'vue'
import
{
CheckCircleOutlined
,
CloseCircleOutlined
,
InfoCircleOutlined
,
QuestionCircleOutlined
}
from
'@ant-design/icons-vue'
import
dayjs
from
'dayjs'
// 审核结果选项接口
interface
ResultOption
{
value
:
'approved'
|
'rejected'
|
string
label
:
string
icon
:
any
className
:
string
color
?:
string
}
// 摘要项接口
interface
SummaryItem
{
label
:
string
value
:
string
key
?:
string
}
// 工作流节点接口
interface
WorkflowNode
{
id
:
string
name
:
string
processor
?:
string
[
key
:
string
]:
any
}
const
props
=
defineProps
({
// 基础配置
title
:
{
type
:
String
,
default
:
'审核意见'
},
showTag
:
{
type
:
Boolean
,
default
:
true
},
tagColor
:
{
type
:
String
,
default
:
'orange'
},
tagText
:
{
type
:
String
,
default
:
'审核节点'
},
// 审核结果配置
resultOptions
:
{
type
:
Array
as
()
=>
ResultOption
[],
default
:
()
=>
[
{
value
:
'approved'
,
label
:
'通过'
,
icon
:
CheckCircleOutlined
,
className
:
'approved'
,
color
:
'#52c41a'
},
{
value
:
'rejected'
,
label
:
'退回'
,
icon
:
CloseCircleOutlined
,
className
:
'rejected'
,
color
:
'#ff4d4f'
import
{
ref
,
onMounted
,
nextTick
}
from
'vue'
import
{
useForm
,
BasicForm
,
FormSchema
}
from
'/@/components/Form'
import
{
CheckCircleOutlined
,
CloseCircleOutlined
}
from
'@ant-design/icons-vue'
import
{
complete
,
getMyTaskFlow
,
getRejectNode
}
from
'/@/components/Process/api/todo'
import
{
message
}
from
'ant-design-vue'
const
emit
=
defineEmits
([
'update:visible'
,
'success'
,
'error'
,
'close'
])
const
props
=
defineProps
({
beforeFlowNode
:
{
type
:
Object
,
default
:
()
=>
({})
},
assignee
:
{
type
:
String
,
default
:
null
},
userType
:
{
type
:
String
,
default
:
''
},
deployId
:
{
type
:
String
,
default
:
''
},
dataId
:
{
type
:
String
,
default
:
''
},
})
const
formSchemas
:
FormSchema
[]
=
[
{
label
:
'审批结果'
,
field
:
'reviewStatus'
,
component
:
'Input'
,
slot
:
'slot_passOrReturn'
,
required
:
true
,
},
{
label
:
'用户类型'
,
field
:
'userType'
,
component
:
'Input'
,
defaultValue
:
props
.
userType
,
ifShow
:
false
,
},
{
label
:
'审核意见'
,
field
:
'comment'
,
component
:
'InputTextArea'
,
required
:
true
,
componentProps
:
{
allowClear
:
true
,
showCount
:
true
,
maxlength
:
512
,
autoSize
:
{
minRows
:
7
,
maxRows
:
10
},
style
:
{
width
:
'100%'
}
}
]
},
defaultResult
:
{
type
:
String
,
default
:
'approved'
},
// 评论配置
commentLabel
:
{
type
:
String
,
default
:
'审核意见'
},
commentPlaceholder
:
{
type
:
String
,
default
:
'请输入审核意见...'
},
commentRows
:
{
type
:
Number
,
default
:
4
},
commentMaxLength
:
{
type
:
Number
,
default
:
500
},
isCommentRequired
:
{
type
:
Boolean
,
default
:
false
},
// 退回时是否强制要求评论
requireCommentOnReject
:
{
type
:
Boolean
,
default
:
true
},
// 退回节点配置
showRejectNodeSelect
:
{
type
:
Boolean
,
default
:
true
},
rejectNodes
:
{
type
:
Array
as
()
=>
WorkflowNode
[],
default
:
()
=>
[]
},
canSelectRejectNode
:
{
type
:
Boolean
,
default
:
true
},
emptyRejectNodesTip
:
{
type
:
String
,
default
:
'当前流程无可退回的历史节点'
},
// 附加信息配置
showAdditionalInfo
:
{
type
:
Boolean
,
default
:
true
},
additionalInfoText
:
{
type
:
String
,
default
:
'退回后,申请人需重新提交申请'
},
// 摘要配置
showSummary
:
{
type
:
Boolean
,
default
:
true
},
summaryItems
:
{
type
:
Array
as
()
=>
SummaryItem
[],
default
:
()
=>
[]
},
// 当前节点信息(用于默认摘要)
currentNode
:
{
type
:
Object
as
()
=>
WorkflowNode
|
null
,
default
:
null
},
currentUser
:
{
type
:
String
,
default
:
''
},
showCurrentTime
:
{
type
:
Boolean
,
default
:
true
},
// 初始数据
initialData
:
{
type
:
Object
as
()
=>
{
result
?:
string
comment
?:
string
rejectNode
?:
string
[
key
:
string
]:
any
},
default
:
()
=>
({})
},
// 其他配置
disabled
:
{
type
:
Boolean
,
default
:
false
},
readonly
:
{
type
:
Boolean
,
default
:
false
}
})
const
emit
=
defineEmits
([
'update:approval-data'
,
'change'
])
// 表单数据
const
approvalForm
=
ref
({
result
:
props
.
defaultResult
as
string
,
comment
:
''
,
rejectNode
:
''
})
// 当前时间
const
currentTime
=
ref
(
new
Date
())
// 退回节点选项
const
rejectNodeOptions
=
computed
(()
=>
{
if
(
!
props
.
rejectNodes
||
props
.
rejectNodes
.
length
===
0
)
{
return
[]
}
return
props
.
rejectNodes
.
map
(
node
=>
({
label
:
node
.
name
,
value
:
node
.
id
}))
})
// 评论是否必填
const
isCommentRequiredComputed
=
computed
(()
=>
{
if
(
props
.
isCommentRequired
)
return
true
if
(
props
.
requireCommentOnReject
&&
approvalForm
.
value
.
result
===
'rejected'
)
return
true
return
false
})
// 评论额外提示
const
commentExtra
=
computed
(()
=>
{
if
(
isCommentRequiredComputed
.
value
)
{
return
'此项为必填项'
}
return
''
})
// 默认摘要项
const
defaultSummaryItems
=
computed
<
SummaryItem
[]
>
(()
=>
{
const
items
:
SummaryItem
[]
=
[]
if
(
props
.
currentNode
)
{
items
.
push
({
label
:
'当前节点'
,
value
:
props
.
currentNode
.
name
})
}
if
(
props
.
currentUser
)
{
items
.
push
({
label
:
'处理人'
,
value
:
props
.
currentUser
})
}
if
(
props
.
showCurrentTime
)
{
items
.
push
({
label
:
'待处理时间'
,
value
:
dayjs
(
currentTime
.
value
).
format
(
'YYYY-MM-DD HH:mm:ss'
)
})
}
return
items
})
// 最终的摘要项(合并默认和自定义)
const
finalSummaryItems
=
computed
(()
=>
{
if
(
props
.
summaryItems
.
length
>
0
)
{
return
props
.
summaryItems
}
return
defaultSummaryItems
.
value
})
// 获取审核数据
function
getApprovalData
()
{
const
data
:
any
=
{
result
:
approvalForm
.
value
.
result
,
comment
:
approvalForm
.
value
.
comment
}
// 如果是退回且需要退回节点信息
if
(
approvalForm
.
value
.
result
===
'rejected'
&&
props
.
showRejectNodeSelect
)
{
const
selectedNode
=
props
.
rejectNodes
.
find
(
node
=>
node
.
id
===
approvalForm
.
value
.
rejectNode
)
data
.
rejectNodeId
=
approvalForm
.
value
.
rejectNode
data
.
rejectNodeName
=
selectedNode
?.
name
||
''
}
// 添加时间戳
data
.
approvalTime
=
new
Date
().
toISOString
()
return
data
}
// 验证表单
async
function
validate
():
Promise
<
{
valid
:
boolean
;
errors
:
string
[]
}
>
{
const
errors
:
string
[]
=
[]
if
(
!
approvalForm
.
value
.
result
)
{
errors
.
push
(
'请选择审核结果'
)
}
if
(
isCommentRequiredComputed
.
value
&&
!
approvalForm
.
value
.
comment
?.
trim
())
{
errors
.
push
(
'请填写审核意见'
)
}
if
(
approvalForm
.
value
.
result
===
'rejected'
&&
props
.
showRejectNodeSelect
)
{
if
(
props
.
canSelectRejectNode
&&
!
approvalForm
.
value
.
rejectNode
)
{
errors
.
push
(
'请选择退回节点'
)
{
label
:
'接收人'
,
field
:
'approvalUser'
,
component
:
'JSelectUser'
,
required
:
true
,
componentProps
:
{
allowClear
:
true
,
rowKey
:
'username'
,
labelKey
:
'realname'
,
showButton
:
false
,
modalTitle
:
'用户'
,
style
:
{
width
:
'100%'
}
},
},
{
label
:
'接收角色'
,
field
:
'approvalRole'
,
component
:
'RoleSelect'
,
required
:
true
,
componentProps
:
{
allowClear
:
true
,
maxSelectCount
:
3
,
isRadioSelection
:
false
,
style
:
{
width
:
'100%'
}
},
}
]
const
[
registerForm
,
{
validate
,
updateSchema
,
setFieldsValue
}]
=
useForm
({
schemas
:
formSchemas
,
showActionButtonGroup
:
false
,
layout
:
'vertical'
,
labelWidth
:
'120px'
,
size
:
'small'
,
baseColProps
:
{
span
:
24
},
})
const
handleClose
=
()
=>
{
emit
(
'update:visible'
,
false
)
emit
(
'close'
)
}
return
{
valid
:
errors
.
length
===
0
,
errors
}
}
// 重置表单
function
resetForm
()
{
approvalForm
.
value
=
{
result
:
props
.
defaultResult
,
comment
:
''
,
rejectNode
:
props
.
rejectNodes
[
0
]?.
id
||
''
}
// 如果有初始数据,则填充
if
(
props
.
initialData
.
result
)
{
approvalForm
.
value
.
result
=
props
.
initialData
.
result
approvalForm
.
value
.
comment
=
props
.
initialData
.
comment
||
''
approvalForm
.
value
.
rejectNode
=
props
.
initialData
.
rejectNode
||
''
}
}
// 设置表单数据
function
setFormData
(
data
:
any
)
{
if
(
data
.
result
!==
undefined
)
{
approvalForm
.
value
.
result
=
data
.
result
}
if
(
data
.
comment
!==
undefined
)
{
approvalForm
.
value
.
comment
=
data
.
comment
}
if
(
data
.
rejectNode
!==
undefined
)
{
approvalForm
.
value
.
rejectNode
=
data
.
rejectNode
}
}
// 监听表单变化
watch
(
approvalForm
,
(
newVal
)
=>
{
const
approvalData
=
{
result
:
newVal
.
result
,
comment
:
newVal
.
comment
,
rejectNode
:
newVal
.
rejectNode
}
emit
(
'update:approval-data'
,
approvalData
)
emit
(
'change'
,
approvalData
)
},
{
deep
:
true
}
)
// 监听初始数据变化
watch
(
()
=>
props
.
initialData
,
(
newData
)
=>
{
if
(
newData
&&
Object
.
keys
(
newData
).
length
>
0
)
{
setFormData
(
newData
)
}
},
{
deep
:
true
,
immediate
:
true
}
)
const
handleSaveApp
=
async
()
=>
{
const
formDataTmp
=
await
validate
()
const
myTaskFlow
=
await
getMyTaskFlow
({
deploymentId
:
props
.
deployId
,
dataId
:
props
.
dataId
})
if
(
myTaskFlow
?.
taskId
)
{
alert
(
JSON
.
stringify
(
myTaskFlow
))
const
reviewStatus
=
formDataTmp
.
reviewStatus
;
if
(
reviewStatus
===
true
)
{
await
complete
({
instanceId
:
myTaskFlow
.
procInsId
||
''
,
deployId
:
myTaskFlow
.
deployId
||
''
,
taskId
:
myTaskFlow
.
taskId
,
comment
:
formDataTmp
.
comment
,
values
:
{
approval
:
formDataTmp
.
approvalUser
||
formDataTmp
.
approvalRole
,
approvalType
:
formDataTmp
.
userType
},
})
}
else
{
const
rejectNode
=
await
getRejectNode
({
deployId
:
props
.
deployId
,
taskId
:
myTaskFlow
.
taskId
,
isApproved
:
false
,
})
alert
(
JSON
.
stringify
(
rejectNode
))
if
(
rejectNode
?.
targetNode
)
{
await
complete
({
instanceId
:
myTaskFlow
.
procInsId
||
''
,
deployId
:
myTaskFlow
.
deployId
||
''
,
taskId
:
myTaskFlow
.
taskId
,
comment
:
formDataTmp
.
comment
,
values
:
{
rejectNode
:
rejectNode
.
targetNode
,
},
})
}
// 监听可退回节点变化,设置默认退回节点
watch
(
()
=>
props
.
rejectNodes
,
(
nodes
)
=>
{
if
(
nodes
&&
nodes
.
length
>
0
&&
!
approvalForm
.
value
.
rejectNode
)
{
approvalForm
.
value
.
rejectNode
=
nodes
[
0
]?.
id
||
''
}
emit
(
'success'
,
props
.
dataId
)
nextTick
(()
=>
{
message
.
success
(
'任务发送成功'
)
handleClose
()
})
}
},
{
immediate
:
true
}
)
// 启动定时器更新时间
let
timer
:
ReturnType
<
typeof
setInterval
>
|
null
=
null
if
(
props
.
showCurrentTime
)
{
timer
=
setInterval
(()
=>
{
currentTime
.
value
=
new
Date
()
},
1000
)
}
// 组件卸载时清除定时器
onUnmounted
(()
=>
{
if
(
timer
)
{
clearInterval
(
timer
)
}
})
// 暴露方法给父组件
defineExpose
({
getApprovalData
,
validate
,
resetForm
,
setFormData
,
getFormRef
:
()
=>
approvalForm
})
onMounted
(
async
()
=>
{
if
(
props
.
userType
===
'user'
)
{
updateSchema
([{
field
:
'approvalUser'
,
required
:
true
,
ifShow
:
true
,
},{
field
:
'approvalRole'
,
required
:
false
,
ifShow
:
false
,
}])
}
else
{
updateSchema
([{
field
:
'approvalRole'
,
required
:
true
,
ifShow
:
true
,
},{
field
:
'approvalUser'
,
required
:
false
,
ifShow
:
false
,
}])
}
})
</
script
>
<
style
scoped
lang=
"scss"
>
.approval-panel
{
background-color
:
#fff
;
border-left
:
1px
solid
#e8eef2
;
flex
:
1
;
display
:
flex
;
flex-direction
:
column
;
overflow
:
hidden
;
height
:
100%
;
background-color
:
#f5f7fa
;
.approval-card
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
background-color
:
#fff
;
:deep
(
.ant-card-head
)
{
padding
:
0
24px
;
background-color
:
#e9ecef
;
border-bottom
:
1px
solid
#e8eef2
;
}
:deep
(
.ant-card-body
)
{
flex
:
1
;
padding
:
24px
;
overflow-y
:
auto
;
}
}
.approval-header
{
padding
:
16px
20px
;
border-bottom
:
1px
solid
#e8eef2
;
background-color
:
#fafbfc
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
flex-shrink
:
0
;
padding
:
16px
0
;
.approval-title
{
font-size
:
16px
;
...
...
@@ -505,149 +231,53 @@ defineExpose({
.approval-content
{
flex
:
1
;
overflow-y
:
auto
;
padding
:
20px
;
&
:
:-
webkit-scrollbar
{
width
:
4px
;
}
&
:
:-
webkit-scrollbar-track
{
background
:
#f1f1f1
;
border-radius
:
2px
;
}
&
:
:-
webkit-scrollbar-thumb
{
background
:
#c1c1c1
;
border-radius
:
2px
;
&
:hover
{
background
:
#a8a8a8
;
}
}
}
.result-group
{
display
:
flex
;
gap
:
16px
;
width
:
100%
;
.ant-radio-wrapper
{
flex
:
1
;
:deep
(
.ant-radio
)
{
display
:
none
;
}
:deep
(
.ant-radio
+
*)
{
padding
:
0
;
}
.radio-content
{
.approval-radio-group
{
width
:
100%
;
display
:
flex
;
gap
:
8px
;
.radio-button
{
flex
:
1
;
text-align
:
center
;
height
:
36px
;
line-height
:
36px
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
padding
:
16px
;
border
:
1px
solid
#d9d9d9
;
border-radius
:
8px
;
transition
:
all
0
.3s
;
cursor
:
pointer
;
.radio-icon
{
font-size
:
28px
;
margin-bottom
:
8px
;
}
.radio-label
{
font-size
:
14px
;
font-weight
:
500
;
}
}
&
.approved
.radio-content
{
&
:hover
{
gap
:
6px
;
font-weight
:
500
;
&
:first-child:not
(
.ant-radio-button-wrapper-checked
)
{
border-color
:
#52c41a
;
background-color
:
#f6ffed
;
}
}
&
.rejected
.radio-content
{
&
:hover
{
border-color
:
#ff4d4f
;
background-color
:
#fff2f0
;
}
}
}
:deep
(
.ant-radio-wrapper-checked
)
{
.approved
.radio-content
{
border-color
:
#52c41a
;
background-color
:
#f6ffed
;
.radio-icon
{
color
:
#52c41a
;
&
:hover
{
border-color
:
#73d13d
;
color
:
#73d13d
;
}
}
}
.rejected
.radio-content
{
border-color
:
#ff4d4f
;
background-color
:
#fff2f0
;
.radio-icon
{
color
:
#ff4d4f
;
}
}
}
}
.form-tip
{
font-size
:
12px
;
color
:
#ff4d4f
;
margin-top
:
4px
;
}
&
:last-child:not
(
.ant-radio-button-wrapper-checked
)
{
border-color
:
#ff4d4f
;
color
:
#ff4d4f
;
.additional-info
{
margin-top
:
16px
;
&
:hover
{
border-color
:
#ff7875
;
color
:
#ff7875
;
}
}
.info-tip
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
padding
:
8px
12px
;
background-color
:
#fff7e6
;
border-radius
:
4px
;
font-size
:
12px
;
color
:
#d46b00
;
.anticon
{
font-size
:
14px
;
}
}
}
&
.ant-radio-button-wrapper-checked
:first-child
{
background
:
#52c41a
;
border-color
:
#52c41a
;
}
.approval-summary
{
background-color
:
#fafbfc
;
border-radius
:
8px
;
padding
:
12px
;
.summary-item
{
display
:
flex
;
justify-content
:
space-between
;
margin-bottom
:
8px
;
font-size
:
13px
;
&
:last-child
{
margin-bottom
:
0
;
}
.summary-label
{
color
:
#8c8c8c
;
}
.summary-value
{
color
:
#262626
;
font-weight
:
500
;
&
.ant-radio-button-wrapper-checked
:last-child
{
background
:
#ff4d4f
;
border-color
:
#ff4d4f
;
}
}
}
}
...
...
zrch-risk-client-39/src/views/common/CurrentFormPanel.vue
浏览文件 @
62b97b82
<
template
>
<div
class=
"form-panel"
>
<
div
class=
"form-header
"
>
<
span
class=
"form-title"
>
当前待办
</span
>
<a-tag
color=
"green"
v-if=
"editableNode"
>
可编辑
</a-tag
>
</div
>
<div
class=
"form-content"
v-if=
"editableNode"
>
<a-card
:title=
"editableNode.name"
:bordered=
"false"
class=
"current-form-card"
>
<
template
#
extra
>
<a-tag
color=
"processing"
>
待处理
</a-tag
>
</
template
>
<
a-card
class=
"form-card"
:bordered=
"false
"
>
<
template
#
title
>
<div
class=
"form-header"
>
<span
class=
"form-title"
>
当前待办[
{{
editableNode
?.
name
||
'无'
}}
]
</span
>
<a-button
color=
"blue"
v-if=
"!isEditStatus"
@
click=
"handleSetEditStatus"
>
编辑
</a-button
>
<a-button
color=
"blue"
v-if=
"isEditStatus"
@
click=
"handleSetEditStatus"
>
只读
</a-button
>
<
/div
>
</
template
>
<div
class=
"form-content"
v-if=
"editableNode"
>
<component
:is=
"getComponent(editableNode.formUrl)"
:ref=
"(el) => setFormRef(el, editableNode.id)"
...
...
@@ -18,11 +18,11 @@
:current-flow-node=
"editableNode"
@
update:form-data=
"handleFormDataUpdate"
/>
</
a-card
>
</div
>
<div
v-else
class=
"empty-form-state"
>
<
a-empty
description=
"未找到可编辑的表单节点"
/
>
</
div
>
</
div
>
<div
v-else
class=
"empty-form-state"
>
<a-empty
description=
"未找到可编辑的表单节点"
/
>
<
/div
>
</
a-card
>
</div>
</template>
...
...
@@ -79,6 +79,8 @@ const modules = import.meta.glob('@/views/**/*.vue')
const
formComponentRef
=
ref
<
FormComponentInstance
|
null
>
(
null
)
const
formData
=
ref
<
any
>
({})
const
isEditStatus
=
ref
(
false
);
// 设置表单组件 ref
function
setFormRef
(
el
:
any
,
nodeId
:
string
)
{
if
(
el
)
{
...
...
@@ -250,6 +252,14 @@ function createErrorComponent(msg: string) {
}
}
// 处理编辑状态切换
function
handleSetEditStatus
()
{
isEditStatus
.
value
=
!
isEditStatus
.
value
if
(
formComponentRef
.
value
&&
typeof
formComponentRef
.
value
.
formDisabled
===
'function'
)
{
//formComponentRef.value.formDisabled(isEditStatus.value)
}
}
// 暴露方法给父组件
defineExpose
({
getFormData
,
...
...
@@ -266,16 +276,40 @@ defineExpose({
display
:
flex
;
flex-direction
:
column
;
overflow
:
hidden
;
background-color
:
#f
ff
;
background-color
:
#f
5f7fa
;
.form-header
{
padding
:
16px
24px
;
border-bottom
:
1px
solid
#e8eef2
;
.form-card
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
background-color
:
#fff
;
:deep
(
.ant-card-head
)
{
padding
:
0
24px
;
background-color
:
#e9ecef
;
border-bottom
:
1px
solid
#e8eef2
;
min-height
:
auto
;
.ant-card-head-wrapper
{
height
:
auto
;
}
}
:deep
(
.ant-card-body
)
{
flex
:
1
;
padding
:
0
;
overflow
:
hidden
;
display
:
flex
;
flex-direction
:
column
;
}
}
.form-header
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
flex-shrink
:
0
;
width
:
100%
;
padding
:
16px
0
;
.form-title
{
font-size
:
16px
;
...
...
@@ -308,28 +342,6 @@ defineExpose({
}
}
.current-form-card
{
border-radius
:
8px
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0
.06
);
border
:
1px
solid
#e8eef2
;
:deep
(
.ant-card-head
)
{
background-color
:
#fafbfc
;
border-bottom
:
1px
solid
#e8eef2
;
padding
:
12px
20px
;
.ant-card-head-title
{
font-size
:
15px
;
font-weight
:
500
;
color
:
#096dd9
;
}
}
:deep
(
.ant-card-body
)
{
padding
:
24px
;
}
}
.empty-form-state
{
flex
:
1
;
display
:
flex
;
...
...
zrch-risk-client-39/src/views/common/FlowHistoryChart.vue
0 → 100644
浏览文件 @
62b97b82
<
template
>
<div
class=
"flowchart-container"
>
<div
v-show=
"loading"
class=
"loading-state"
>
<a-spin
tip=
"加载流程图中..."
size=
"large"
/>
</div>
<div
v-show=
"!loading"
class=
"bpmn-wrapper"
>
<div
class=
"bpmn-content"
>
<bpmn-viewer
:flow-data=
"flowData"
:procInsId=
"procInsId"
/>
</div>
</div>
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
ref
,
watch
}
from
'vue'
;
import
{
message
}
from
'ant-design-vue'
;
import
{
flowXmlAndNode
}
from
'/@/components/Process/api/definition'
;
import
BpmnViewer
from
'/@/components/Process/viewer/index.vue'
;
// Emits
const
emit
=
defineEmits
([
'loaded'
]);
// 响应式数据
const
loading
=
ref
(
false
);
const
flowData
=
ref
<
any
>
({});
const
procInsId
=
ref
(
''
);
const
loadData
=
async
(
data
)
=>
{
procInsId
.
value
=
data
.
procInsId
||
''
;
const
deployId
=
data
.
deployId
||
''
;
try
{
loading
.
value
=
true
;
const
res
=
await
flowXmlAndNode
({
procInsId
:
procInsId
.
value
,
deployId
:
deployId
});
loading
.
value
=
false
;
flowData
.
value
=
res
;
}
catch
(
error
)
{
console
.
error
(
'加载流程图失败:'
,
error
);
message
.
error
(
'加载流程图失败'
);
throw
error
;
}
finally
{
loading
.
value
=
false
;
}
};
// 重置数据
const
resetData
=
()
=>
{
flowData
.
value
=
{};
};
// 暴露方法给父组件
defineExpose
({
loadData
,
resetData
,
});
</
script
>
<
style
lang=
"scss"
scoped
>
.flowchart-container
{
height
:
100%
;
padding
:
24px
;
background
:
#f5f7fa
;
overflow
:
hidden
;
}
.bpmn-wrapper
{
height
:
100%
;
.bpmn-content
{
height
:
100%
;
min-height
:
500px
;
border
:
1px
solid
#e8e8e8
;
border-radius
:
6px
;
overflow
:
hidden
;
background
:
white
;
}
}
.loading-state
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
height
:
100%
;
min-height
:
300px
;
margin-top
:
20px
;
}
@media
(
max-width
:
768px
)
{
.flowchart-container
{
padding
:
16px
;
}
}
</
style
>
\ No newline at end of file
zrch-risk-client-39/src/views/common/FlowHistoryDrawer.vue
0 → 100644
浏览文件 @
62b97b82
<
template
>
<BasicDrawer
v-bind=
"$attrs"
title=
"流程追踪"
width=
"100%"
@
register=
"registerBasicDrawer"
:header-style=
"
{ backgroundColor: '#018ffb', borderBottom: '1px solid #e8eef2' }">
<div
class=
"drawer-content"
>
<FlowHistoryChart
ref=
"refFlowHistoryChart"
/>
<FlowHistoryRecord
:procInsId=
"procInsId"
/>
</div>
</BasicDrawer>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
defineComponent
,
ref
}
from
'vue'
;
import
{
BasicDrawer
,
useDrawerInner
}
from
'/@/components/Drawer'
;
import
FlowHistoryChart
from
'./FlowHistoryChart.vue'
;
import
FlowHistoryRecord
from
'./FlowHistoryRecord.vue'
;
const
procInsId
=
ref
(
''
);
const
dataId
=
ref
(
''
);
const
deployId
=
ref
(
''
);
const
refFlowHistoryChart
=
ref
();
const
callback
=
(
data
)
=>
{
refFlowHistoryChart
.
value
.
loadData
(
data
);
}
const
[
registerBasicDrawer
,
{
closeDrawer
,
setDrawerProps
}]
=
useDrawerInner
(
callback
);
const
handleCloseDrawer
=
()
=>
{
closeDrawer
();
}
</
script
>
\ No newline at end of file
zrch-risk-client-39/src/views/common/FlowHistoryRecord.vue
0 → 100644
浏览文件 @
62b97b82
<
template
>
<div
class=
"record-container"
>
<div
v-if=
"loading"
class=
"loading-state"
>
<a-spin
tip=
"加载流转记录中..."
/>
</div>
<div
v-else-if=
"flowRecordList.length === 0"
class=
"empty-state"
>
<a-empty
description=
"暂无流转记录"
/>
</div>
<a-timeline
v-else
class=
"flow-timeline"
>
<a-timeline-item
v-for=
"(item, index) in flowRecordList"
:key=
"index"
:color=
"getTimelineColor(item.finishTime)"
>
<div
class=
"timeline-item"
>
<div
class=
"timeline-header"
>
<div
class=
"task-name"
>
<a-tag
:color=
"getTaskStatusColor(item.finishTime)"
class=
"status-tag"
>
{{
item
.
finishTime
?
'已完成'
:
'进行中'
}}
</a-tag>
<span
class=
"task-title"
>
{{
item
.
taskName
}}
</span>
</div>
<div
class=
"task-time"
>
<ClockCircleOutlined
/>
<span>
{{
formatDuration
(
item
.
duration
)
}}
</span>
</div>
</div>
<div
class=
"timeline-content"
>
<a-descriptions
size=
"small"
:column=
"1"
bordered
>
<a-descriptions-item
label=
"办理人"
v-if=
"item.assigneeName"
>
<UserOutlined
class=
"desc-icon"
/>
{{
item
.
assigneeName
}}
<a-tag
v-if=
"item.deptName"
color=
"default"
size=
"small"
>
{{
item
.
deptName
}}
</a-tag>
</a-descriptions-item>
<a-descriptions-item
label=
"候选办理"
v-if=
"item.candidate"
>
<TeamOutlined
class=
"desc-icon"
/>
{{
item
.
candidate
}}
</a-descriptions-item>
<a-descriptions-item
label=
"接收时间"
>
<CalendarOutlined
class=
"desc-icon"
/>
{{
formatTime
(
item
.
createTime
)
}}
</a-descriptions-item>
<a-descriptions-item
label=
"处理时间"
v-if=
"item.finishTime"
>
<CheckCircleOutlined
class=
"desc-icon"
/>
{{
formatTime
(
item
.
finishTime
)
}}
</a-descriptions-item>
<a-descriptions-item
label=
"处理意见"
v-if=
"item.comment?.comment"
>
<FormOutlined
class=
"desc-icon"
/>
<div
class=
"comment-box"
>
{{
item
.
comment
.
comment
}}
</div>
</a-descriptions-item>
</a-descriptions>
</div>
</div>
</a-timeline-item>
</a-timeline>
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
ref
,
watch
}
from
'vue'
;
import
{
message
}
from
'ant-design-vue'
;
import
{
UserOutlined
,
CalendarOutlined
,
ClockCircleOutlined
,
FormOutlined
,
TeamOutlined
,
CheckCircleOutlined
,
}
from
'@ant-design/icons-vue'
;
import
{
flowRecord
}
from
'/@/components/Process/api/finished'
;
import
dayjs
from
'dayjs'
;
// Props
const
props
=
defineProps
<
{
procInsId
:
string
;
}
>
();
// 响应式数据
const
loading
=
ref
(
false
);
const
flowRecordList
=
ref
<
any
[]
>
([]);
// 方法
const
formatTime
=
(
time
:
string
)
=>
{
if
(
!
time
)
return
''
;
return
dayjs
(
time
).
format
(
'YYYY-MM-DD HH:mm:ss'
);
};
const
formatDuration
=
(
duration
:
string
)
=>
{
if
(
!
duration
)
return
''
;
return
duration
.
replace
(
'PT'
,
''
).
toLowerCase
();
};
const
getTimelineColor
=
(
finishTime
:
string
|
null
)
=>
{
return
finishTime
?
'green'
:
'blue'
;
};
const
getTaskStatusColor
=
(
finishTime
:
string
|
null
)
=>
{
return
finishTime
?
'success'
:
'processing'
;
};
const
loadData
=
async
(
data
)
=>
{
try
{
loading
.
value
=
true
;
const
res
=
await
flowRecord
({
procInsId
:
props
.
procInsId
});
flowRecordList
.
value
=
res
.
flowList
||
[];
}
catch
(
error
)
{
console
.
error
(
'加载流转记录失败:'
,
error
);
message
.
error
(
'加载流转记录失败'
);
throw
error
;
}
finally
{
loading
.
value
=
false
;
}
};
// 重置数据
const
resetData
=
()
=>
{
flowRecordList
.
value
=
[];
};
// 暴露方法给父组件
defineExpose
({
loadData
,
resetData
,
});
</
script
>
<
style
lang=
"scss"
scoped
>
.record-container
{
height
:
100%
;
padding
:
24px
;
background
:
#f5f7fa
;
overflow-y
:
auto
;
}
.flow-timeline
{
:deep
(
.ant-timeline-item
)
{
padding-bottom
:
24px
;
&
:last-child
{
padding-bottom
:
0
;
}
}
}
.timeline-item
{
background
:
white
;
border-radius
:
8px
;
padding
:
16px
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0
.06
);
.timeline-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
margin-bottom
:
12px
;
flex-wrap
:
wrap
;
gap
:
8px
;
.task-name
{
display
:
flex
;
align-items
:
center
;
gap
:
12px
;
flex-wrap
:
wrap
;
.status-tag
{
border-radius
:
12px
;
font-size
:
12px
;
padding
:
2px
10px
;
}
.task-title
{
font-weight
:
500
;
color
:
#333
;
}
}
.task-time
{
color
:
#666
;
font-size
:
13px
;
.anticon
{
margin-right
:
4px
;
}
}
}
.timeline-content
{
:deep
(
.ant-descriptions
)
{
border-radius
:
6px
;
overflow
:
hidden
;
.ant-descriptions-item-label
{
background
:
#fafafa
;
font-weight
:
500
;
min-width
:
80px
;
}
.desc-icon
{
margin-right
:
8px
;
color
:
#666
;
}
}
}
.comment-box
{
white-space
:
pre-wrap
;
word-break
:
break-word
;
line-height
:
1
.6
;
color
:
#333
;
}
}
.loading-state
,
.empty-state
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
height
:
100%
;
min-height
:
300px
;
}
@media
(
max-width
:
768px
)
{
.record-container
{
padding
:
16px
;
}
.timeline-item
{
padding
:
12px
;
}
}
</
style
>
\ No newline at end of file
zrch-risk-client-39/src/views/common/HistoryPanel.vue
浏览文件 @
62b97b82
<
template
>
<div
class=
"history-panel"
:class=
"
{ 'empty-history': readonlyNodes.length === 0 }">
<div
class=
"history-header"
>
<span
class=
"history-title"
>
历史节点
</span>
<a-tag
color=
"blue"
v-if=
"readonlyNodes.length > 0"
>
{{
readonlyNodes
.
length
}}
个节点
</a-tag>
</div>
<div
class=
"history-content"
:class=
"
{ 'has-scroll': readonlyNodes.length > 3 }">
<div
v-if=
"readonlyNodes.length === 0"
class=
"empty-history-state"
>
<a-empty
description=
"暂无历史节点"
:image=
"simpleImage"
/>
</div>
<a-timeline
v-else
>
<a-timeline-item
v-for=
"(node, index) in readonlyNodes"
:key=
"node.id"
:color=
"index === readonlyNodes.length - 1 ? 'blue' : 'gray'"
>
<template
#
dot
>
<div
class=
"timeline-dot"
>
<check-circle-outlined
v-if=
"index
<
readonlyNodes
.
length
-
1
"
/>
<clock-circle-outlined
v-else
/>
</div>
</
template
>
<a-card
:title=
"node.name"
:bordered=
"false"
size=
"small"
class=
"history-card"
:class=
"{ 'last-card': index === readonlyNodes.length - 1 }"
<div
class=
"history-panel-wrapper"
>
<a-card
class=
"history-card-container"
:bordered=
"false"
:body-style=
"
{ padding: 0, height: '100%', display: 'flex', flexDirection: 'column' }">
<template
#
title
>
<div
class=
"history-header"
>
<span
class=
"history-title"
>
历史节点
</span>
<a-tag
color=
"blue"
v-if=
"readonlyNodes.length > 0"
>
{{
readonlyNodes
.
length
}}
个节点
</a-tag>
</div>
</
template
>
<div
class=
"history-content"
:class=
"{ 'empty-history': readonlyNodes.length === 0, 'has-scroll': readonlyNodes.length > 3 }"
>
<div
v-if=
"readonlyNodes.length === 0"
class=
"empty-history-state"
>
<a-empty
description=
"暂无历史节点"
:image=
"simpleImage"
/>
</div>
<a-timeline
v-else
>
<a-timeline-item
v-for=
"(node, index) in readonlyNodes"
:key=
"node.id"
:color=
"index === readonlyNodes.length - 1 ? 'blue' : 'gray'"
>
<
template
#
extra
>
<a-tag
:color=
"index === readonlyNodes.length - 1 ? 'orange' : 'green'"
size=
"small"
>
{{
index
===
readonlyNodes
.
length
-
1
?
'已通过'
:
'已完成'
}}
</a-tag>
</
template
>
<div
class=
"history-card-content"
>
<div
class=
"node-info"
>
<span
class=
"node-label"
>
表单名称:
</span>
<span
class=
"node-value"
>
{{ node.formName || node.name }}
</span>
<
template
#
dot
>
<div
class=
"timeline-dot"
>
<check-circle-outlined
v-if=
"index
<
readonlyNodes
.
length
-
1
"
/>
<clock-circle-outlined
v-else
/>
</div>
<div
class=
"node-info"
>
<span
class=
"node-label"
>
处理时间:
</span>
<span
class=
"node-value"
>
{{ node.processTime || '--' }}
</span>
</div>
</div>
<!-- 显示历史表单数据 -->
<
template
v-if=
"showHistoryFormData"
>
<a-divider
style=
"margin: 12px 0"
/>
<div
class=
"history-form-preview"
>
<div
class=
"preview-header"
>
<span>
表单数据预览
</span>
<a-button
type=
"link"
size=
"small"
@
click=
"togglePreview(node.id)"
>
{{
expandedPreviewId
===
node
.
id
?
'收起'
:
'展开'
}}
</a-button>
</
template
>
<a-card
:title=
"node.name"
:bordered=
"false"
size=
"small"
class=
"history-node-card"
:class=
"{ 'last-card': index === readonlyNodes.length - 1 }"
>
<
template
#
extra
>
<a-tag
:color=
"index === readonlyNodes.length - 1 ? 'orange' : 'green'"
size=
"small"
>
{{
index
===
readonlyNodes
.
length
-
1
?
'已通过'
:
'已完成'
}}
</a-tag>
</
template
>
<div
class=
"history-card-content"
>
<div
class=
"node-info"
>
<span
class=
"node-label"
>
表单名称:
</span>
<span
class=
"node-value"
>
{{ node.formName || node.name }}
</span>
</div>
<div
v-show=
"expandedPreviewId === node.id"
class=
"form-preview-content"
>
<component
:is=
"getComponent(node)"
:ref=
"(el) => setFormRef(el, node.id)"
:disabled=
"true"
:readonly=
"true"
:form-data=
"
{ dataId: props.dataId, nodeId: node.id }"
:dataId="props.dataId"
:current-flow-node="node"
class="history-form-component"
/>
<div
class=
"node-info"
>
<span
class=
"node-label"
>
处理时间:
</span>
<span
class=
"node-value"
>
{{ node.processTime || '--' }}
</span>
</div>
</div>
</
template
>
</a-card>
</a-timeline-item>
</a-timeline>
</div>
<!-- 显示历史表单数据 -->
<
template
v-if=
"showHistoryFormData"
>
<a-divider
style=
"margin: 12px 0"
/>
<div
class=
"history-form-preview"
>
<div
class=
"preview-header"
>
<span>
表单数据预览
</span>
<a-button
type=
"link"
size=
"small"
@
click=
"togglePreview(node.id)"
>
{{
expandedPreviewId
===
node
.
id
?
'收起'
:
'展开'
}}
</a-button>
</div>
<div
v-show=
"expandedPreviewId === node.id"
class=
"form-preview-content"
>
<component
:is=
"getComponent(node)"
:ref=
"(el) => setFormRef(el, node.id)"
:disabled=
"true"
:readonly=
"true"
:form-data=
"
{ dataId: props.dataId, nodeId: node.id }"
:dataId="props.dataId"
:current-flow-node="node"
class="history-form-component"
/>
</div>
</div>
</
template
>
</a-card>
</a-timeline-item>
</a-timeline>
</div>
</a-card>
</div>
</template>
...
...
@@ -314,7 +318,7 @@ watch(expandedPreviewId, async (newId, oldId) => {
</
script
>
<
style
scoped
lang=
"scss"
>
.history-panel
{
.history-panel
-wrapper
{
width
:
50%
;
background-color
:
#f5f7fa
;
border-right
:
1px
solid
#e8eef2
;
...
...
@@ -322,22 +326,35 @@ watch(expandedPreviewId, async (newId, oldId) => {
flex-direction
:
column
;
overflow
:
hidden
;
&
.empty-history
{
.history-content
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
.history-card-container
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
background-color
:
#f5f7fa
;
:deep
(
.ant-card-head
)
{
padding
:
0
20px
;
background-color
:
#e9ecef
;
// 灰色背景,比整体背景稍深
border-bottom
:
1px
solid
#e8eef2
;
min-height
:
auto
;
.ant-card-head-wrapper
{
height
:
auto
;
}
}
:deep
(
.ant-card-body
)
{
flex
:
1
;
overflow
:
hidden
;
}
}
.history-header
{
padding
:
16px
20px
;
border-bottom
:
1px
solid
#e8eef2
;
background-color
:
#fff
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
flex-shrink
:
0
;
width
:
100%
;
padding
:
16px
0
;
.history-title
{
font-size
:
16px
;
...
...
@@ -351,6 +368,12 @@ watch(expandedPreviewId, async (newId, oldId) => {
overflow-y
:
auto
;
padding
:
16px
12px
;
&
.empty-history
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
&
.has-scroll
{
padding-right
:
8px
;
}
...
...
@@ -406,7 +429,7 @@ watch(expandedPreviewId, async (newId, oldId) => {
}
}
.history-card
{
.history-
node-
card
{
background-color
:
#fff
;
border-radius
:
8px
;
margin-bottom
:
8px
;
...
...
zrch-risk-client-39/src/views/common/TaskAssigneeDrawer.vue
浏览文件 @
62b97b82
<
template
>
<a-drawer
:title=
"title
"
title=
"任务指派
"
:visible=
"visible"
:width=
"width
"
width=
"30%
"
:closable=
"true"
:mask-closable=
"
maskClosabl
e"
:destroy-on-close=
"
destroyOnClos
e"
:mask-closable=
"
fals
e"
:destroy-on-close=
"
tru
e"
:footer=
"null"
@
close=
"handleClose"
class=
"task-assignee-drawer"
>
<div
class=
"drawer-content"
>
<div
class=
"assignee-section"
>
<a-card
:title=
"assigneeTitle"
:bordered=
"false"
class=
"assignee-card"
>
<a-form
layout=
"vertical"
ref=
"formRef"
>
<a-form-item
label=
"用户类型"
required
>
<a-radio-group
v-model:value=
"localUserType"
@
change=
"handleUserTypeChange"
disabled
>
<a-radio
value=
"user"
>
<user-outlined
/>
用户
</a-radio>
<a-radio
value=
"role"
>
<team-outlined
/>
角色
</a-radio>
</a-radio-group>
</a-form-item>
<!-- 用户选择区域 - 未选择时显示选择器 -->
<a-form-item
v-if=
"localUserType === 'user' && !userAssigneeId"
label=
"选择用户"
:required=
"required"
:validate-status=
"userValidateStatus"
:help=
"userValidateHelp"
>
<div
class=
"assignee-selector"
>
<a-input
:value=
"userName"
placeholder=
"点击'选择'按钮选择用户"
readonly
class=
"assignee-input"
>
<template
#
suffix
>
<a-button
type=
"link"
size=
"small"
@
click=
"handleSelectUser"
>
选择
</a-button>
</
template
>
</a-input>
<a-button
v-if=
"userAssigneeId"
type=
"text"
size=
"small"
@
click=
"clearAssignee"
class=
"clear-btn"
>
<close-circle-outlined
/>
</a-button>
</div>
</a-form-item>
<!-- 已指定用户信息展示 -->
<div
v-if=
"localUserType === 'user' && userAssigneeId"
class=
"assignee-info-wrapper"
>
<div
class=
"assignee-info-label"
>
已指定用户:
</div>
<a-descriptions
:column=
"1"
size=
"small"
bordered
>
<a-descriptions-item
label=
"用户ID"
>
{{ userAssigneeId }}
</a-descriptions-item>
<a-descriptions-item
label=
"用户名称"
>
{{ userName || '--' }}
</a-descriptions-item>
</a-descriptions>
<!-- <a-button
type="link"
size="small"
@click="clearAssignee"
class="change-btn"
<a-card
title=
"选择任务指派人"
:bordered=
"false"
class=
"assignee-card"
>
<a-form
layout=
"vertical"
>
<a-form-item
label=
"用户类型"
required
>
<a-radio-group
v-model:value=
"localUserType"
disabled
>
<a-radio
value=
"user"
>
<UserOutlined
/>
用户
</a-radio>
<a-radio
value=
"role"
>
<TeamOutlined
/>
角色
</a-radio>
</a-radio-group>
</a-form-item>
<!-- 用户/角色选择区域(未选择时) -->
<a-form-item
v-if=
"!hasAssignee"
:label=
"localUserType === 'user' ? '选择用户' : '选择角色'"
:required=
"required"
:validate-status=
"validateStatus"
:help=
"validateHelp"
>
<div
class=
"assignee-selector"
>
<a-input
:value=
"assigneeDisplayName"
:placeholder=
"`点击'选择'按钮选择$
{localUserType === 'user' ? '用户' : '角色'}`"
readonly
class="assignee-input"
>
重新选择
</a-button> -->
<template
#
suffix
>
<a-button
type=
"link"
size=
"small"
@
click=
"handleSelect"
>
选择
</a-button>
</
template
>
</a-input>
</div>
<!-- 角色选择区域 - 未选择时显示选择器 -->
<a-form-item
v-if=
"localUserType === 'role' && !roleAssigneeId"
label=
"选择角色"
:required=
"required"
:validate-status=
"roleValidateStatus"
:help=
"roleValidateHelp"
>
<div
class=
"assignee-selector"
>
<a-input
:value=
"roleName"
placeholder=
"点击'选择'按钮选择角色"
readonly
class=
"assignee-input"
>
<
template
#
suffix
>
<a-button
type=
"link"
size=
"small"
@
click=
"handleSelectRole"
>
选择
</a-button>
</
template
>
</a-input>
<a-button
v-if=
"roleAssigneeId"
type=
"text"
size=
"small"
@
click=
"clearAssignee"
class=
"clear-btn"
>
<close-circle-outlined
/>
</a-button>
</div>
</a-form-item>
<!-- 已指定角色信息展示 -->
<div
v-if=
"localUserType === 'role' && roleAssigneeId"
class=
"assignee-info-wrapper"
>
<div
class=
"assignee-info-label"
>
已指定角色:
</div>
<a-descriptions
:column=
"1"
size=
"small"
bordered
>
<a-descriptions-item
label=
"角色ID"
>
{{ roleAssigneeId }}
</a-descriptions-item>
<a-descriptions-item
label=
"角色名称"
>
{{ roleName || '--' }}
</a-descriptions-item>
</a-descriptions>
<!-- <a-button
type="link"
size="small"
@click="clearAssignee"
class="change-btn"
>
重新选择
</a-button> -->
</div>
</a-form>
<div
class=
"assignee-actions"
>
<a-space>
<a-button
@
click=
"handleCancel"
>
取消
</a-button>
<a-button
type=
"primary"
:loading=
"confirmLoading"
@
click=
"handleConfirm"
>
确认
</a-button>
</a-space>
</a-form-item>
<!-- 已指定信息展示 -->
<div
v-if=
"hasAssignee"
class=
"assignee-info-wrapper"
>
<div
class=
"assignee-info-label"
>
已指定{{ localUserType === 'user' ? '用户' : '角色' }}:
</div>
<a-descriptions
:column=
"1"
size=
"small"
bordered
>
<a-descriptions-item
:label=
"localUserType === 'user' ? '用户ID' : '角色ID'"
>
{{ assigneeId }}
</a-descriptions-item>
<a-descriptions-item
:label=
"localUserType === 'user' ? '用户名' : '角色名'"
>
{{ assigneeDisplayName || '--' }}
</a-descriptions-item>
</a-descriptions>
<a-button
type=
"link"
size=
"small"
@
click=
"clearAssignee"
class=
"change-btn"
>
重新选择
</a-button>
</div>
</a-card>
</div>
</a-form>
<div
class=
"assignee-actions"
>
<a-space>
<a-button
@
click=
"handleClose"
>
取消
</a-button>
<a-button
type=
"primary"
:loading=
"confirmLoading"
@
click=
"handleConfirm"
>
确认
</a-button>
</a-space>
</div>
</a-card>
</div>
<RoleSelectModal
...
...
@@ -152,306 +92,169 @@
</template>
<
script
lang=
"ts"
setup
>
import
{
ref
,
computed
,
watch
,
onMounted
}
from
'vue'
import
{
ref
,
computed
,
watch
,
nextTick
}
from
'vue'
import
{
message
}
from
'ant-design-vue'
import
{
useModal
}
from
'/@/components/Modal'
import
{
UserOutlined
,
TeamOutlined
,
CloseCircleOutlined
}
from
'@ant-design/icons-vue'
import
{
UserOutlined
,
TeamOutlined
}
from
'@ant-design/icons-vue'
import
UserSelectModal
from
'/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue'
import
RoleSelectModal
from
'/@/components/Form/src/jeecg/components/modal/RoleSelectModal.vue'
import
{
complete
,
getMyTaskFlow
}
from
'/@/components/Process/api/todo'
const
props
=
defineProps
({
// 抽屉基础配置
visible
:
{
type
:
Boolean
,
default
:
false
},
title
:
{
type
:
String
,
default
:
'任务分配'
},
width
:
{
type
:
[
Number
,
String
],
default
:
600
},
maskClosable
:
{
type
:
Boolean
,
default
:
false
},
destroyOnClose
:
{
type
:
Boolean
,
default
:
true
},
formData
:
{
type
:
Object
,
default
:
()
=>
({})
},
// 初始分配数据
assignee
:
{
type
:
String
,
default
:
null
},
// 分配组件配置
assigneeTitle
:
{
type
:
String
,
default
:
'选择处理人'
},
deployId
:
{
type
:
String
,
default
:
''
},
userType
:
{
type
:
String
,
default
:
'user'
},
required
:
{
type
:
Boolean
,
default
:
true
},
beforeFlowNode
:
{
type
:
Object
,
default
:
()
=>
({})
},
currentFlowNode
:
{
type
:
Object
,
default
:
()
=>
({})
},
nextFlowNode
:
{
type
:
Object
,
default
:
()
=>
({})
}
visible
:
{
type
:
Boolean
,
default
:
false
},
formData
:
{
type
:
Object
,
default
:
()
=>
({})
},
assignee
:
{
type
:
String
,
default
:
null
},
userType
:
{
type
:
String
,
default
:
'user'
},
deployId
:
{
type
:
String
,
default
:
''
},
required
:
{
type
:
Boolean
,
default
:
true
},
assigneeName
:
{
type
:
String
,
default
:
null
},
dataId
:
{
type
:
String
,
default
:
''
}
})
const
emit
=
defineEmits
([
'update:visible'
,
'success'
,
'error'
,
'close'
,
'assignee-confirm'
])
const
emit
=
defineEmits
([
'update:visible'
,
'success'
,
'error'
,
'close'
])
const
[
registerSelUserModal
,
{
openModal
:
userOpenModal
}]
=
useModal
()
const
[
registerSelRoleModal
,
{
openModal
:
roleOpenModal
}]
=
useModal
()
// 状态
const
localUserType
=
ref
<
'user'
|
'role'
>
(
'user'
)
const
userAssigneeId
=
ref
(
''
)
const
userName
=
ref
(
''
)
const
roleAssigneeId
=
ref
(
''
)
const
roleName
=
ref
(
''
)
const
assigneeId
=
ref
(
''
)
const
assigneeDisplayName
=
ref
(
''
)
const
confirmLoading
=
ref
(
false
)
const
drawerHistoryVisible
=
ref
(
false
)
// 计算属性
const
hasAssignee
=
computed
(()
=>
!!
assigneeId
.
value
)
/**
* 初始化数据 - 修复核心问题
*/
const
validateStatus
=
computed
(()
=>
{
if
(
!
props
.
required
||
hasAssignee
.
value
)
return
''
return
'error'
})
const
validateHelp
=
computed
(()
=>
validateStatus
.
value
===
'error'
?
`请选择
${
localUserType
.
value
===
'user'
?
'用户'
:
'角色'
}
`
:
''
)
// 初始化数据
const
initData
=
()
=>
{
console
.
log
(
'initData called, assignee:'
,
props
.
assignee
,
'userType:'
,
props
.
userType
)
// 清空现有数据
userAssigneeId
.
value
=
''
userName
.
value
=
''
roleAssigneeId
.
value
=
''
roleName
.
value
=
''
assigneeId
.
value
=
''
assigneeDisplayName
.
value
=
''
if
(
props
.
assignee
)
{
// 根据 userType 决定显示类型
if
(
props
.
userType
===
'role'
)
{
localUserType
.
value
=
'role'
roleAssigneeId
.
value
=
props
.
assignee
// 如果有 name 属性可以传入,但当前 props 没有,可以后续扩展
}
else
{
localUserType
.
value
=
'user'
userAssigneeId
.
value
=
props
.
assignee
// 如果有 name 属性可以传入,但当前 props 没有,可以后续扩展
localUserType
.
value
=
props
.
userType
===
'role'
?
'role'
:
'user'
assigneeId
.
value
=
props
.
assignee
if
(
props
.
assigneeName
)
{
assigneeDisplayName
.
value
=
props
.
assigneeName
}
}
else
{
// 没有 assignee 时使用默认类型
localUserType
.
value
=
props
.
userType
===
'role'
?
'role'
:
'user'
}
console
.
log
(
'initData result:'
,
{
localUserType
:
localUserType
.
value
,
userAssigneeId
:
userAssigneeId
.
value
,
roleAssigneeId
:
roleAssigneeId
.
value
})
}
/**
* 设置用户信息(供外部调用,用于传入用户名)
*/
const
setUserInfo
=
(
userId
:
string
,
userNameValue
:
string
)
=>
{
userAssigneeId
.
value
=
userId
userName
.
value
=
userNameValue
localUserType
.
value
=
'user'
}
/**
* 设置角色信息(供外部调用,用于传入角色名)
*/
const
setRoleInfo
=
(
roleId
:
string
,
roleNameValue
:
string
)
=>
{
roleAssigneeId
.
value
=
roleId
roleName
.
value
=
roleNameValue
localUserType
.
value
=
'role'
}
// 表单验证状态
const
userValidateStatus
=
computed
(()
=>
{
if
(
!
props
.
required
)
return
''
if
(
localUserType
.
value
!==
'user'
)
return
''
return
!
userAssigneeId
.
value
?
'error'
:
''
})
const
userValidateHelp
=
computed
(()
=>
userValidateStatus
.
value
===
'error'
?
'请选择用户'
:
''
)
const
roleValidateStatus
=
computed
(()
=>
{
if
(
!
props
.
required
)
return
''
if
(
localUserType
.
value
!==
'role'
)
return
''
return
!
roleAssigneeId
.
value
?
'error'
:
''
})
const
roleValidateHelp
=
computed
(()
=>
roleValidateStatus
.
value
===
'error'
?
'请选择角色'
:
''
)
function
handleUserTypeChange
()
{
clearAssignee
()
const
resetDrawer
=
()
=>
{
localUserType
.
value
=
props
.
userType
===
'role'
?
'role'
:
'user'
assigneeId
.
value
=
''
assigneeDisplayName
.
value
=
''
}
function
handleSelectUser
()
{
userOpenModal
()
const
clearAssignee
=
()
=>
{
assigneeId
.
value
=
''
assigneeDisplayName
.
value
=
''
}
function
handleSelectRole
()
{
roleOpenModal
()
const
handleSelect
=
()
=>
{
if
(
localUserType
.
value
===
'user'
)
{
userOpenModal
()
}
else
{
roleOpenModal
()
}
}
// 选择用户回调
function
onSelectUserOk
(
options
:
any
[],
values
:
any
[])
{
if
(
!
values
||
values
.
length
===
0
)
return
userAssigneeId
.
value
=
values
[
0
]
userName
.
value
=
options
[
0
]?.
label
||
''
const
onSelectUserOk
=
(
options
:
any
[],
values
:
any
[])
=>
{
alert
(
JSON
.
stringify
(
options
))
if
(
!
values
?.
length
)
return
assigneeId
.
value
=
values
[
0
]
assigneeDisplayName
.
value
=
options
[
0
]?.
label
||
''
localUserType
.
value
=
'user'
}
// 选择角色回调
function
onSelectRoleOk
(
options
:
any
[],
values
:
any
[])
{
if
(
!
values
||
values
.
length
===
0
)
return
roleAssigneeId
.
value
=
values
[
0
]
roleName
.
value
=
options
[
0
]?.
label
||
''
const
onSelectRoleOk
=
(
options
:
any
[],
values
:
any
[])
=>
{
alert
(
JSON
.
stringify
(
options
))
if
(
!
values
?.
length
)
return
assigneeId
.
value
=
values
[
0
]
assigneeDisplayName
.
value
=
options
[
0
]?.
label
||
''
localUserType
.
value
=
'role'
}
function
clearAssignee
()
{
userAssigneeId
.
value
=
''
userName
.
value
=
''
roleAssigneeId
.
value
=
''
roleName
.
value
=
''
}
function
handleCancel
()
{
handleClose
()
const
handleClose
=
()
=>
{
emit
(
'update:visible'
,
false
)
emit
(
'close'
)
}
async
function
handleConfirm
()
{
// 验证
if
(
props
.
required
)
{
if
(
localUserType
.
value
===
'user'
&&
!
userAssigneeId
.
value
)
{
message
.
error
(
'请选择用户'
)
return
}
if
(
localUserType
.
value
===
'role'
&&
!
roleAssigneeId
.
value
)
{
message
.
error
(
'请选择角色'
)
return
}
const
handleConfirm
=
async
()
=>
{
if
(
props
.
required
&&
!
assigneeId
.
value
)
{
message
.
error
(
`请选择
${
localUserType
.
value
===
'user'
?
'用户'
:
'角色'
}
`
)
return
}
confirmLoading
.
value
=
true
try
{
await
handleSendTask
()
setTimeout
(()
=>
{
handleClose
()
},
500
)
}
catch
(
error
:
any
)
{
message
.
error
(
error
?.
message
||
'提交失败,请重试'
)
emit
(
'error'
,
error
)
}
finally
{
confirmLoading
.
value
=
false
const
dataId
=
props
.
dataId
?.
id
||
''
if
(
dataId
&&
props
.
deployId
)
{
const
myTaskFlow
=
await
getMyTaskFlow
({
deploymentId
:
props
.
deployId
,
dataId
})
if
(
myTaskFlow
?.
taskId
)
{
await
complete
({
instanceId
:
myTaskFlow
.
procInsId
||
''
,
deployId
:
myTaskFlow
.
deployId
||
''
,
taskId
:
myTaskFlow
.
taskId
,
comment
:
''
,
values
:
{
approval
:
assigneeId
.
value
,
approvalType
:
localUserType
.
value
},
})
}
}
emit
(
'success'
,
props
.
dataId
)
nextTick
(()
=>
{
message
.
success
(
'任务发送成功'
)
handleClose
()
})
}
catch
(
error
:
any
)
{
emit
(
'error'
,
error
)
}
finally
{
confirmLoading
.
value
=
false
}
}
}
function
handleClose
()
{
emit
(
'update:visible'
,
false
)
emit
(
'close'
)
}
// 对外暴露的方法
const
getAssigneeData
=
()
=>
({
userType
:
localUserType
.
value
,
assignee
:
assigneeId
.
value
,
name
:
assigneeDisplayName
.
value
,
remark
:
''
})
function
resetDrawer
()
{
localUserType
.
value
=
props
.
userType
===
'role'
?
'role'
:
'user'
userAssigneeId
.
value
=
''
userName
.
value
=
''
roleAssigneeId
.
value
=
''
roleName
.
value
=
''
const
setUserInfo
=
(
userId
:
string
,
userNameValue
:
string
)
=>
{
assigneeId
.
value
=
userId
assigneeDisplayName
.
value
=
userNameValue
localUserType
.
value
=
'user'
}
function
getAssigneeData
()
{
return
{
userType
:
localUserType
.
value
,
assignee
:
localUserType
.
value
===
'user'
?
userAssigneeId
.
value
:
roleAssigneeId
.
value
,
name
:
localUserType
.
value
===
'user'
?
userName
.
value
:
roleName
.
value
,
remark
:
''
}
const
setRoleInfo
=
(
roleId
:
string
,
roleNameValue
:
string
)
=>
{
assigneeId
.
value
=
roleId
assigneeDisplayName
.
value
=
roleNameValue
localUserType
.
value
=
'role'
}
const
handleSendTask
=
async
()
=>
{
try
{
const
submitData
=
{
instanceId
:
""
,
deployId
:
""
,
taskId
:
""
,
comment
:
''
,
values
:
{},
approval
:
''
,
approvalType
:
''
,
}
const
openHistoryDrawer
=
()
=>
{
drawerHistoryVisible
.
value
=
true
;
};
submitData
.
values
[
'approval'
]
=
localUserType
.
value
===
'user'
?
userAssigneeId
.
value
:
roleAssigneeId
.
value
submitData
.
values
[
'approvalType'
]
=
localUserType
.
value
===
'user'
?
'user'
:
'role'
const
dataId
=
props
.
formData
.
id
||
''
if
(
dataId
)
{
try
{
const
myTaskFlow
=
await
getMyTaskFlow
({
deploymentId
:
props
.
deployId
,
dataId
:
dataId
})
console
.
log
(
'获取流程任务信息88888888888888888:'
,
myTaskFlow
)
if
(
myTaskFlow
?.
taskId
)
{
submitData
.
taskId
=
myTaskFlow
.
taskId
submitData
.
deployId
=
myTaskFlow
.
deployId
submitData
.
instanceId
=
myTaskFlow
.
procInsId
}
await
complete
(
submitData
)
emit
(
'success'
,
dataId
)
message
.
success
(
'任务发送成功'
)
}
catch
(
e
)
{
console
.
warn
(
'flowTaskInfo 获取 taskId 失败'
,
e
)
throw
e
// 重新抛出错误,让外层 catch 处理
}
}
else
{
// 没有 dataId 时的处理
emit
(
'success'
,
null
)
message
.
success
(
'操作成功'
)
}
}
catch
(
error
:
any
)
{
console
.
error
(
'handleSendTask error:'
,
error
)
throw
error
}
}
// 监听 visible 变化
watch
(()
=>
props
.
visible
,
(
newVal
)
=>
{
...
...
@@ -462,14 +265,10 @@ watch(() => props.visible, (newVal) => {
}
},
{
immediate
:
true
})
// 监听 assignee 变化
watch
(()
=>
props
.
assignee
,
()
=>
{
if
(
props
.
visible
)
{
initData
()
}
if
(
props
.
visible
)
initData
()
})
// 监听 userType 变化
watch
(()
=>
props
.
userType
,
()
=>
{
if
(
props
.
visible
&&
!
props
.
assignee
)
{
localUserType
.
value
=
props
.
userType
===
'role'
?
'role'
:
'user'
...
...
@@ -538,91 +337,77 @@ defineExpose({
}
}
.assignee-
section
{
.assignee-
card
{
height
:
100%
;
border-radius
:
8px
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0
.06
);
.assignee-card
{
height
:
100%
;
border-radius
:
8px
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0
.06
);
:deep
(
.ant-card-head
)
{
background-color
:
#fafbfc
;
border-bottom
:
1px
solid
#e8eef2
;
.ant-card-head-title
{
font-size
:
16px
;
font-weight
:
500
;
}
}
:deep
(
.ant-card-head
)
{
background-color
:
#fafbfc
;
border-bottom
:
1px
solid
#e8eef2
;
:deep
(
.ant-card-body
)
{
padding
:
24px
;
height
:
calc
(
100%
-
57px
);
overflow-y
:
auto
;
display
:
flex
;
flex-direction
:
column
;
.ant-card-head-title
{
font-size
:
16px
;
font-weight
:
500
;
}
}
.assignee-selector
{
:deep
(
.ant-card-body
)
{
padding
:
24px
;
height
:
calc
(
100%
-
57px
);
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
.assignee-input
{
flex
:
1
;
:deep
(
.ant-input
)
{
background-color
:
#fafafa
;
cursor
:
pointer
;
&
:hover
{
background-color
:
#fff
;
}
}
}
flex-direction
:
column
;
}
}
.assignee-selector
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
.assignee-input
{
flex
:
1
;
.clear-btn
{
color
:
#999
;
font-size
:
14px
;
:deep
(
.ant-input
)
{
background-color
:
#fafafa
;
cursor
:
pointer
;
&
:hover
{
color
:
#ff4d4
f
;
background-color
:
#ff
f
;
}
}
}
}
.assignee-info-wrapper
{
margin-top
:
12px
;
padding
:
12px
;
background-color
:
#fafbfc
;
border-radius
:
8px
;
.assignee-info-wrapper
{
margin-top
:
12px
;
padding
:
12px
;
background-color
:
#fafbfc
;
border-radius
:
8px
;
.assignee-info-label
{
margin-bottom
:
8px
;
font-weight
:
500
;
color
:
#1f2f3d
;
}
.change-btn
{
margin-top
:
8px
;
padding-left
:
0
;
}
:deep
(
.ant-descriptions
)
{
.ant-descriptions-item-label
{
background-color
:
#f5f5f5
;
width
:
80px
;
}
}
.assignee-info-label
{
margin-bottom
:
8px
;
font-weight
:
500
;
color
:
#1f2f3d
;
}
.change-btn
{
margin-top
:
8px
;
padding-left
:
0
;
}
.assignee-actions
{
margin-top
:
auto
;
padding-top
:
24px
;
text-align
:
center
;
border-top
:
1px
solid
#f0f0f0
;
:deep
(
.ant-descriptions
)
{
.ant-descriptions-item-label
{
background-color
:
#f5f5f5
;
width
:
80px
;
}
}
}
.assignee-actions
{
margin-top
:
auto
;
padding-top
:
24px
;
text-align
:
center
;
border-top
:
1px
solid
#f0f0f0
;
}
</
style
>
\ No newline at end of file
zrch-risk-client-39/src/views/common/TaskAssigneeSelector.vue
deleted
100644 → 0
浏览文件 @
54da6b43
<
template
>
<div
class=
"task-assignee-selector"
>
<a-card
:title=
"title"
:bordered=
"false"
class=
"assignee-card"
>
<template
#
extra
>
<a-tag
:color=
"assigneeStatus.color"
>
{{
assigneeStatus
.
text
}}
</a-tag>
</
template
>
<a-form
layout=
"vertical"
>
<!-- 分配类型选择 -->
<a-form-item
label=
"分配类型"
required
>
<a-radio-group
v-model:value=
"formData.userType"
@
change=
"handleUserTypeChange"
>
<a-radio
value=
"user"
>
<user-outlined
/>
用户
</a-radio>
<a-radio
value=
"role"
>
<team-outlined
/>
角色
</a-radio>
</a-radio-group>
</a-form-item>
<!-- 用户选择区域 -->
<div
v-if=
"formData.userType === 'user'"
class=
"assignee-section"
>
<a-form-item
label=
"选择用户"
required
:validate-status=
"userValidateStatus"
:help=
"userValidateHelp"
>
<div
class=
"assignee-selector"
>
<a-input
:value=
"formData.userName"
placeholder=
"点击'选择'按钮选择用户"
readonly
class=
"assignee-input"
>
<
template
#
suffix
>
<a-button
type=
"link"
size=
"small"
@
click=
"handleSelectUser"
>
选择
</a-button>
</
template
>
</a-input>
<a-button
v-if=
"formData.assignee"
type=
"text"
size=
"small"
@
click=
"clearAssignee"
class=
"clear-btn"
>
<close-circle-outlined
/>
</a-button>
</div>
</a-form-item>
<div
v-if=
"formData.assignee"
class=
"assignee-info"
>
<a-descriptions
:column=
"1"
size=
"small"
bordered
>
<a-descriptions-item
label=
"用户ID"
>
{{ formData.assignee }}
</a-descriptions-item>
<a-descriptions-item
label=
"用户名称"
>
{{ formData.userName }}
</a-descriptions-item>
<a-descriptions-item
label=
"用户类型"
><a-tag
color=
"blue"
>
系统用户
</a-tag></a-descriptions-item>
</a-descriptions>
</div>
</div>
<!-- 角色选择区域 -->
<div
v-if=
"formData.userType === 'role'"
class=
"assignee-section"
>
<a-form-item
label=
"选择角色"
required
:validate-status=
"roleValidateStatus"
:help=
"roleValidateHelp"
>
<div
class=
"assignee-selector"
>
<a-input
:value=
"formData.roleName"
placeholder=
"点击'选择'按钮选择角色"
readonly
class=
"assignee-input"
>
<
template
#
suffix
>
<a-button
type=
"link"
size=
"small"
@
click=
"handleSelectRole"
>
选择
</a-button>
</
template
>
</a-input>
<a-button
v-if=
"formData.assignee"
type=
"text"
size=
"small"
@
click=
"clearAssignee"
class=
"clear-btn"
>
<close-circle-outlined
/>
</a-button>
</div>
</a-form-item>
<div
v-if=
"formData.assignee"
class=
"assignee-info"
>
<a-descriptions
:column=
"1"
size=
"small"
bordered
>
<a-descriptions-item
label=
"角色ID"
>
{{ formData.assignee }}
</a-descriptions-item>
<a-descriptions-item
label=
"角色名称"
>
{{ formData.roleName }}
</a-descriptions-item>
<a-descriptions-item
label=
"角色类型"
><a-tag
color=
"purple"
>
系统角色
</a-tag></a-descriptions-item>
</a-descriptions>
</div>
</div>
<!-- 备注信息 -->
<a-form-item
label=
"备注"
>
<a-textarea
v-model:value=
"formData.remark"
placeholder=
"请输入备注信息(选填)"
:rows=
"3"
:maxlength=
"200"
show-count
/>
</a-form-item>
</a-form>
<div
class=
"assignee-actions"
>
<a-space>
<a-button
@
click=
"handleCancel"
>
取消
</a-button>
<a-button
type=
"primary"
:loading=
"confirmLoading"
@
click=
"handleConfirm"
:disabled=
"!isValid"
>
确认
</a-button>
</a-space>
</div>
</a-card>
<RoleSelectModal
rowKey=
"id"
@
register=
"registerSelRoleModal"
@
getSelectResult=
"onSelectOk"
isRadioSelection
:showButton=
"false"
labelKey=
"roleName"
/>
<UserSelectModal
rowKey=
"id"
@
register=
"registerSelUserModal"
@
getSelectResult=
"onSelectOk"
isRadioSelection
:showButton=
"false"
labelKey=
"realname"
/>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
{
ref
,
computed
,
watch
}
from
'vue'
import
{
message
}
from
'ant-design-vue'
import
{
useModal
}
from
'/@/components/Modal'
import
{
UserOutlined
,
TeamOutlined
,
CloseCircleOutlined
}
from
'@ant-design/icons-vue'
import
UserSelectModal
from
'/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue'
import
RoleSelectModal
from
'/@/components/Form/src/jeecg/components/modal/RoleSelectModal.vue'
interface
AssigneeData
{
userType
:
'user'
|
'role'
assignee
:
string
userName
?:
string
roleName
?:
string
remark
?:
string
}
const
props
=
defineProps
({
title
:
{
type
:
String
,
default
:
'任务分配'
},
procDefId
:
{
type
:
String
,
required
:
true
},
taskId
:
{
type
:
String
,
required
:
true
},
formData
:
{
type
:
Object
,
default
:
()
=>
({})
},
initialAssignee
:
{
type
:
Object
as
()
=>
AssigneeData
|
null
,
default
:
null
},
userType
:
{
type
:
String
,
default
:
'user'
},
required
:
{
type
:
Boolean
,
default
:
true
}
})
const
emit
=
defineEmits
([
'confirm'
,
'cancel'
,
'success'
,
'error'
])
const
[
registerSelUserModal
,
{
openModal
:
userOpenModal
}]
=
useModal
()
const
[
registerSelRoleModal
,
{
openModal
:
roleOpenModal
}]
=
useModal
()
// 修复:使用函数初始化,确保能正确读取 props 值
const
getInitialUserType
=
():
'user'
|
'role'
=>
{
// 优先使用 initialAssignee 中的 userType
if
(
props
.
initialAssignee
?.
userType
)
{
return
props
.
initialAssignee
.
userType
}
// 其次使用 props.userType
if
(
props
.
userType
===
'role'
)
{
return
'role'
}
return
'user'
}
// 表单数据
const
formData
=
ref
<
AssigneeData
>
({
userType
:
getInitialUserType
(),
assignee
:
props
.
initialAssignee
?.
assignee
||
''
,
userName
:
props
.
initialAssignee
?.
userName
||
''
,
roleName
:
props
.
initialAssignee
?.
roleName
||
''
,
remark
:
props
.
initialAssignee
?.
remark
||
''
})
const
confirmLoading
=
ref
(
false
)
const
selectedItem
=
ref
<
any
>
(
null
)
// 验证状态
const
userValidateStatus
=
computed
(()
=>
{
if
(
!
props
.
required
)
return
''
return
formData
.
value
.
userType
===
'user'
&&
!
formData
.
value
.
assignee
?
'error'
:
''
})
const
userValidateHelp
=
computed
(()
=>
userValidateStatus
.
value
===
'error'
?
'请选择用户'
:
''
)
const
roleValidateStatus
=
computed
(()
=>
{
if
(
!
props
.
required
)
return
''
return
formData
.
value
.
userType
===
'role'
&&
!
formData
.
value
.
assignee
?
'error'
:
''
})
const
roleValidateHelp
=
computed
(()
=>
roleValidateStatus
.
value
===
'error'
?
'请选择角色'
:
''
)
// 分配状态
const
assigneeStatus
=
computed
(()
=>
{
if
(
!
formData
.
value
.
assignee
)
return
{
text
:
'未分配'
,
color
:
'default'
}
if
(
formData
.
value
.
userType
===
'user'
)
{
return
{
text
:
`已分配用户:
${
formData
.
value
.
userName
}
`
,
color
:
'green'
}
}
return
{
text
:
`已分配角色:
${
formData
.
value
.
roleName
}
`
,
color
:
'blue'
}
})
const
isValid
=
computed
(()
=>
!
props
.
required
||
!!
formData
.
value
.
assignee
)
function
handleUserTypeChange
()
{
clearAssignee
()
}
function
handleSelectUser
()
{
userOpenModal
()
}
function
handleSelectRole
()
{
roleOpenModal
()
}
// 选择回调
function
onSelectOk
(
options
:
any
[],
values
:
any
[])
{
if
(
!
values
||
values
.
length
===
0
)
return
selectedItem
.
value
=
values
[
0
]
const
selectedOption
=
options
[
0
]
if
(
formData
.
value
.
userType
===
'user'
)
{
formData
.
value
.
assignee
=
selectedItem
.
value
.
id
formData
.
value
.
userName
=
selectedOption
.
label
message
.
success
(
`已选择用户:
${
formData
.
value
.
userName
}
`
)
}
else
{
formData
.
value
.
assignee
=
selectedItem
.
value
.
id
formData
.
value
.
roleName
=
selectedOption
.
label
message
.
success
(
`已选择角色:
${
formData
.
value
.
roleName
}
`
)
}
}
function
clearAssignee
()
{
formData
.
value
.
assignee
=
''
formData
.
value
.
userName
=
''
formData
.
value
.
roleName
=
''
}
function
handleCancel
()
{
emit
(
'cancel'
)
}
async
function
handleConfirm
()
{
if
(
props
.
required
&&
!
formData
.
value
.
assignee
)
{
message
.
error
(
formData
.
value
.
userType
===
'user'
?
'请选择用户'
:
'请选择角色'
)
return
}
confirmLoading
.
value
=
true
try
{
const
assigneeData
=
{
userType
:
formData
.
value
.
userType
,
assignee
:
formData
.
value
.
assignee
,
name
:
formData
.
value
.
userType
===
'user'
?
formData
.
value
.
userName
:
formData
.
value
.
roleName
,
remark
:
formData
.
value
.
remark
}
emit
(
'confirm'
,
assigneeData
)
emit
(
'success'
,
assigneeData
)
message
.
success
(
'任务提交成功'
)
}
catch
(
error
:
any
)
{
message
.
error
(
error
?.
message
||
'提交失败,请重试'
)
emit
(
'error'
,
error
)
}
finally
{
confirmLoading
.
value
=
false
}
}
function
getAssigneeData
()
{
return
{
userType
:
formData
.
value
.
userType
,
assignee
:
formData
.
value
.
assignee
,
name
:
formData
.
value
.
userType
===
'user'
?
formData
.
value
.
userName
:
formData
.
value
.
roleName
,
remark
:
formData
.
value
.
remark
}
}
function
resetForm
()
{
formData
.
value
=
{
userType
:
'user'
,
assignee
:
''
,
userName
:
''
,
roleName
:
''
,
remark
:
''
}
}
// 监听 initialAssignee 变化
watch
(()
=>
props
.
initialAssignee
,
(
newVal
)
=>
{
if
(
newVal
)
{
formData
.
value
=
{
userType
:
newVal
.
userType
||
getInitialUserType
(),
assignee
:
newVal
.
assignee
||
''
,
userName
:
newVal
.
userName
||
''
,
roleName
:
newVal
.
roleName
||
''
,
remark
:
newVal
.
remark
||
''
}
}
},
{
immediate
:
true
,
deep
:
true
})
// 修复:监听 userType prop 变化,确保 radio 能正确响应
watch
(()
=>
props
.
userType
,
(
newVal
)
=>
{
if
(
newVal
===
'role'
||
newVal
===
'user'
)
{
// 只有当当前没有分配人时才自动切换类型
if
(
!
formData
.
value
.
assignee
)
{
formData
.
value
.
userType
=
newVal
}
}
},
{
immediate
:
true
})
// immediate: true 确保立即执行
defineExpose
({
getAssigneeData
,
resetForm
,
validate
:
()
=>
isValid
.
value
})
</
script
>
<
style
scoped
lang=
"scss"
>
.task-assignee-selector
{
height
:
100%
;
.assignee-card
{
height
:
100%
;
border-radius
:
8px
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0
.06
);
:deep
(
.ant-card-head
)
{
background-color
:
#fafbfc
;
border-bottom
:
1px
solid
#e8eef2
;
.ant-card-head-title
{
font-size
:
16px
;
font-weight
:
500
;
}
}
:deep
(
.ant-card-body
)
{
padding
:
24px
;
height
:
calc
(
100%
-
57px
);
overflow-y
:
auto
;
display
:
flex
;
flex-direction
:
column
;
}
}
.assignee-section
{
margin-bottom
:
16px
;
}
.assignee-selector
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
.assignee-input
{
flex
:
1
;
:deep
(
.ant-input
)
{
background-color
:
#fafafa
;
cursor
:
pointer
;
&
:hover
{
background-color
:
#fff
;
}
}
}
.clear-btn
{
color
:
#999
;
font-size
:
14px
;
&
:hover
{
color
:
#ff4d4f
;
}
}
}
.assignee-info
{
margin-top
:
12px
;
:deep
(
.ant-descriptions
)
{
.ant-descriptions-item-label
{
background-color
:
#fafbfc
;
width
:
80px
;
}
}
}
.assignee-actions
{
margin-top
:
auto
;
padding-top
:
24px
;
text-align
:
center
;
border-top
:
1px
solid
#f0f0f0
;
}
}
</
style
>
\ No newline at end of file
zrch-risk-client-39/src/views/common/WorkFlowFormDrawer.vue
浏览文件 @
62b97b82
...
...
@@ -8,55 +8,57 @@
:header-style=
"
{ backgroundColor: '#018ffb', borderBottom: '1px solid #e8eef2' }"
@close="handleClose"
class="workflow-form-drawer"
style="padding: 0px;
"
:body-style="{ padding: '5px' }
"
>
<div
class=
"drawer-layout"
:class=
"
{ 'three-columns': showApprovalPanel }">
<HistoryPanel
:readonly-nodes=
"readonlyNodes"
:data-id=
"dataId"
:show-history-form-data=
"props.showHistoryFormData"
/>
<div
class=
"form-wrapper"
>
<CurrentFormPanel
ref=
"currentFormPanelRef"
:editable-node=
"editableNode"
:data-id=
"dataId"
:external-form-data=
"externalFormData"
:form-data=
"props.formData"
:form-bpm=
"formBpm"
:disabled=
"showApprovalPanel||false"
@
update:form-data=
"handleFormDataUpdate"
@
form-mounted=
"handleFormMounted"
/>
</div>
<div
v-if=
"showApprovalPanel"
class=
"approval-wrapper"
>
<slot
name=
"approval-panel"
:approval-data=
"approvalData"
:node=
"editableNode"
>
<ApprovalPanel
ref=
"approvalPanelRef"
:title=
"approvalPanelTitle"
:current-node=
"editableNode"
:current-user=
"currentUser"
:reject-nodes=
"rejectNodes"
:can-select-reject-node=
"canSelectRejectNode"
:initial-data=
"initialApprovalData"
:show-summary=
"showApprovalSummary"
:result-options=
"approvalResultOptions"
:default-result=
"defaultApprovalResult"
:comment-placeholder=
"commentPlaceholder"
:require-comment-on-reject=
"requireCommentOnReject"
:show-reject-node-select=
"showRejectNodeSelect"
:show-approval-panel=
"true"
@
update:approval-data=
"handleApprovalDataUpdate"
/>
</slot>
</div>
<div
class=
"drawer-container"
>
<a-row
:gutter=
"10"
class=
"drawer-row"
:wrap=
"false"
>
<!-- 左侧历史面板 -->
<a-col
:width=
"props.leftPanelWidth"
>
<HistoryPanel
style=
"width: 100%;height: 100%;"
:readonly-nodes=
"readonlyNodes"
:data-id=
"dataId"
:show-history-form-data=
"props.showHistoryFormData"
/>
</a-col>
<!-- 中间表单区域 -->
<a-col
:width=
"props.centerPanelWidth"
>
<CurrentFormPanel
style=
"width: 100%;height: 100%;"
ref=
"currentFormPanelRef"
:editable-node=
"editableNode"
:data-id=
"dataId"
:external-form-data=
"externalFormData"
:form-data=
"props.formData"
:form-bpm=
"formBpm"
:disabled=
"showApprovalPanel || false"
@
update:form-data=
"handleFormDataUpdate"
@
form-mounted=
"handleFormMounted"
/>
</a-col>
<!-- 右侧审核面板 -->
<a-col
v-if=
"showApprovalPanel"
:width=
"props.rightPanelWidth"
>
<ApprovalPanel
style=
"width: 100%;height: 100%;"
ref=
"approvalPanelRef"
:current-node=
"editableNode"
:deploy-id=
"props.deployId"
:data-id=
"props.dataId"
:assignee=
"props.assignee"
:user-type=
"props.userType"
@
update:approval-data=
"handleApprovalDataUpdate"
@
success=
"handleApprovalSuccess"
/>
</a-col>
</a-row>
</div>
<template
#
footer
>
<div
class=
"drawer-footer"
>
</div>
<template
#
extra
>
<a-button
@
click=
"openHistoryDrawer()"
type=
"primary"
>
流程追踪
</a-button>
<a-button
@
click=
"handleClose()"
type=
"primary"
>
关闭抽屉
</a-button>
</
template
>
<FlowHistoryDrawer
@
register=
"refFlowHistoryDrawer"
/>
</a-drawer>
</template>
...
...
@@ -66,12 +68,14 @@ import { message } from 'ant-design-vue'
import
HistoryPanel
from
'./HistoryPanel.vue'
import
CurrentFormPanel
from
'./CurrentFormPanel.vue'
import
ApprovalPanel
from
'./ApprovalPanel.vue'
import
FlowHistoryDrawer
from
'./FlowHistoryDrawer.vue'
;
import
{
useDrawer
}
from
'/@/components/Drawer'
;
const
formBpm
=
ref
(
true
)
const
formDisabled
=
ref
(
false
)
const
drawerHistoryVisible
=
ref
(
false
)
const
[
refFlowHistoryDrawer
,
{
openDrawer
}]
=
useDrawer
();
interface
WorkflowNode
{
id
:
string
...
...
@@ -80,6 +84,7 @@ interface WorkflowNode {
formUrl
?:
string
formListUrl
?:
string
procDefId
?:
string
procInsId
?:
string
dataId
?:
string
processTime
?:
string
processor
?:
string
...
...
@@ -126,10 +131,16 @@ const props = defineProps({
type
:
Object
as
()
=>
Record
<
string
,
any
>
,
default
:
()
=>
({})
},
procDef
Id
:
{
deploy
Id
:
{
type
:
String
,
default
:
''
},
procInsId
:
{
type
:
String
,
default
:
''
},
dataId
:
{
type
:
String
,
default
:
''
...
...
@@ -144,15 +155,7 @@ const props = defineProps({
type
:
Boolean
,
default
:
false
},
approvalPanelTitle
:
{
type
:
String
,
default
:
'审核意见'
},
submitButtonText
:
{
type
:
String
,
default
:
'提交'
},
// 审核结果配置
approvalResultOptions
:
{
type
:
Array
,
...
...
@@ -171,52 +174,27 @@ const props = defineProps({
}
]
},
defaultApprovalResult
:
{
type
:
String
,
default
:
'approved'
},
// 审核意见配置
commentPlaceholder
:
{
type
:
String
,
default
:
'请输入审核意见...'
},
requireCommentOnReject
:
{
type
:
Boolean
,
default
:
true
},
// 退回节点配置
showRejectNodeSelect
:
{
type
:
Boolean
,
default
:
true
},
rejectNodes
:
{
flowNodes
:
{
type
:
Array
as
()
=>
WorkflowNode
[],
default
:
()
=>
[]
},
canSelectRejectNode
:
{
type
:
Boolean
,
default
:
true
},
// 其他配置
currentUser
:
{
type
:
String
,
default
:
''
},
initialApprovalData
:
{
type
:
Object
as
()
=>
{
result
?:
string
comment
?:
string
rejectNode
?:
string
},
default
:
()
=>
({})
currentFlowNodeIdx
:
{
type
:
Number
,
default
:
0
},
showApprovalSummary
:
{
type
:
Boolean
,
default
:
true
}
assignee
:
{
type
:
String
,
default
:
null
},
userType
:
{
type
:
String
,
default
:
'user'
},
// 面板宽度配置
leftPanelWidth
:
{
type
:
String
,
default
:
'40%'
},
centerPanelWidth
:
{
type
:
String
,
default
:
'40%'
},
rightPanelWidth
:
{
type
:
String
,
default
:
'20%'
}
})
const
emit
=
defineEmits
([
'update:visible'
,
'submit'
,
'close'
,
'form-data-update'
])
...
...
@@ -232,7 +210,6 @@ const approvalPanelRef = ref<InstanceType<typeof ApprovalPanel> | null>(null)
const
drawerTitle
=
computed
(()
=>
props
.
title
)
const
drawerWidth
=
computed
(()
=>
props
.
width
)
// 只读节点:索引小于 currentNodeIndex 的节点
const
readonlyNodes
=
computed
(()
=>
{
if
(
!
props
.
workflowNodes
||
props
.
workflowNodes
.
length
===
0
)
{
...
...
@@ -266,11 +243,18 @@ function handleApprovalDataUpdate(data: any) {
approvalData
.
value
=
data
}
// 处理表单组件挂载
function
handleFormMounted
({
nodeId
,
instance
}:
{
nodeId
:
string
;
instance
:
any
})
{
console
.
log
(
'表单组件已挂载 - 节点:'
,
nodeId
)
}
// 处理审核成功
function
handleApprovalSuccess
(
dataId
:
string
)
{
if
(
dataId
===
props
.
dataId
)
{
message
.
success
(
'审核成功'
)
handleClose
()
}
}
// 提交处理
async
function
handleSubmit
()
{
if
(
!
editableNode
.
value
)
{
...
...
@@ -283,7 +267,6 @@ async function handleSubmit() {
return
}
// 如果显示审核面板,验证审核数据
if
(
props
.
showApprovalPanel
&&
approvalPanelRef
.
value
)
{
const
result
=
await
approvalPanelRef
.
value
.
validate
()
if
(
!
result
.
valid
)
{
...
...
@@ -295,17 +278,12 @@ async function handleSubmit() {
submitLoading
.
value
=
true
try
{
// 1. 表单验证
const
isValid
=
await
currentFormPanelRef
.
value
.
validateForm
()
if
(
!
isValid
)
{
message
.
error
(
'请完善表单信息'
)
return
}
// 2. 获取表单数据
const
submitData
=
await
currentFormPanelRef
.
value
.
getFormData
()
// 3. 构建提交数据
const
finalSubmitData
:
any
=
{
nodeId
:
editableNode
.
value
.
id
,
nodeName
:
editableNode
.
value
.
name
,
...
...
@@ -346,13 +324,21 @@ function resetFormData() {
if
(
currentFormPanelRef
.
value
)
{
currentFormPanelRef
.
value
.
resetFormData
()
}
if
(
approvalPanelRef
.
value
)
{
approvalPanelRef
.
value
.
resetForm
()
if
(
approvalPanelRef
.
value
&&
props
.
showApprovalPanel
)
{
(
approvalPanelRef
.
value
as
any
)
.
resetForm
()
}
currentFormData
.
value
=
{}
approvalData
.
value
=
{}
}
const
openHistoryDrawer
=
()
=>
{
openDrawer
(
true
,{
procInsId
:
props
.
procInsId
,
dataId
:
props
.
dataId
,
deployId
:
props
.
deployId
,
}
);
};
// 监听抽屉打开
watch
(()
=>
props
.
visible
,
async
(
newVal
)
=>
{
if
(
newVal
)
{
...
...
@@ -413,8 +399,7 @@ defineExpose({
:deep
(
.ant-drawer-header
)
{
background-color
:
#f5f7fa
;
border-bottom
:
1px
solid
#e8eef2
;
padding
:
16px
24px
;
padding
:
10px
10px
;
.ant-drawer-title
{
font-size
:
16px
;
font-weight
:
500
;
...
...
@@ -431,66 +416,53 @@ defineExpose({
}
:deep
(
.ant-drawer-body
)
{
padding
:
0
;
padding
:
5px
;
height
:
100%
;
overflow
:
hidden
;
}
:deep
(
.ant-drawer-footer
)
{
padding
:
12px
24
px
;
padding
:
5px
10
px
;
border-top
:
1px
solid
#e8eef2
;
background-color
:
red
;
background-color
:
#fff
;
}
}
.drawer-layout
{
display
:
flex
;
.drawer-container
{
height
:
100%
;
overflow
:
hidden
;
// 三栏布局
&
.three-columns
{
.history-panel
{
width
:
30%
;
}
.form-wrapper
{
width
:
40%
;
}
.approval-wrapper
{
width
:
30%
;
}
}
// 两栏布局
&
:not
(
.three-columns
)
{
.history-panel
{
width
:
40%
;
}
.form-wrapper
{
flex
:
1
;
}
}
background
:
#eef5f6
80
;
padding
:
1px
;
margin
:
0
;
}
.
form-wrapper
{
overflow
:
hidden
;
display
:
flex
;
flex-direction
:
column
;
.
drawer-row
{
height
:
100%
;
flex-wrap
:
nowrap
;
background
:
#0c37a4
18
;
}
.approval-wrapper
{
overflow
:
hidden
;
display
:
flex
;
flex-direction
:
column
;
.drawer-col
{
height
:
100%
;
&
:not
(
:last-child
)
{
margin-right
:
16px
;
}
}
.panel-card
{
height
:
100%
;
:deep
(
.ant-card-body
)
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
}
}
.drawer-footer
{
display
:
flex
;
justify-content
:
flex-end
;
height
:
2px
;
background
:
#018ffb
;
gap
:
12px
;
}
</
style
>
\ No newline at end of file
zrch-risk-client-39/src/views/flowable/task/myProcess/components/Detail.vue
浏览文件 @
62b97b82
...
...
@@ -206,7 +206,7 @@ if (key === '3') {
deployId
:
taskForm
.
deployId
,
}).
then
((
res
)
=>
{
flowData
.
value
=
res
console
.
log
(
"xml"
,
JSON
.
stringify
(
res
))
console
.
log
(
"xml
88888888888888888
"
,
JSON
.
stringify
(
res
))
})
}
}
...
...
zrch-risk-client-39/src/views/flowable/task/myProcess/components/Send.vue
浏览文件 @
62b97b82
...
...
@@ -92,6 +92,7 @@ const handleClick = (key: string) => {
flowXmlAndNode
({
deployId
:
deployId
.
value
}).
then
(
res
=>
{
flowData
.
value
=
res
})
}
}
...
...
zrch-risk-client-39/src/views/project/problemArchive/StProblemCheckArchiveList.vue
浏览文件 @
62b97b82
...
...
@@ -36,6 +36,7 @@
<StProblemCheckArchiveModal
@
register=
"registerModal"
@
success=
"handleSuccess"
></StProblemCheckArchiveModal>
<!-- 审批记录 -->
<BpmPictureModal
@
register=
"registerBpmModal"
/>
</div>
</template>
...
...
@@ -55,10 +56,14 @@
const
[
registerBpmModal
,
{
openModal
:
bpmPicModal
}]
=
useModal
();
const
queryParam
=
reactive
<
any
>
({});
//注册model
const
refFlowHistory
=
ref
();
const
deployId
=
ref
(
''
);
const
dataId
=
ref
(
''
);
// 审批记录抽屉
const
drawerHistoryVisible
=
ref
(
false
);
const
[
registerModal
,
{
openModal
}]
=
useModal
();
//注册table数据
const
{
prefixCls
,
tableContext
,
onExportXls
,
onImportXls
}
=
useListPage
({
tableProps
:{
title
:
'问题归档'
,
...
...
@@ -102,31 +107,20 @@
})
const
[
registerTable
,
{
reload
},{
rowSelection
,
selectedRowKeys
}]
=
tableContext
// 高级查询配置
const
superQueryConfig
=
reactive
(
superQuerySchema
);
/**
* 高级查询事件
*/
function
handleSuperQuery
(
params
)
{
Object
.
keys
(
params
).
map
((
k
)
=>
{
queryParam
[
k
]
=
params
[
k
];
});
reload
();
}
/**
* 新增事件
*/
function
handleAdd
()
{
openModal
(
true
,
{
isUpdate
:
false
,
showFooter
:
true
,
});
}
/**
* 编辑事件
*/
function
handleEdit
(
record
:
Recordable
)
{
openModal
(
true
,
{
record
,
...
...
@@ -134,9 +128,6 @@
showFooter
:
true
,
});
}
/**
* 详情
*/
function
handleDetail
(
record
:
Recordable
)
{
openModal
(
true
,
{
record
,
...
...
@@ -144,39 +135,40 @@
showFooter
:
false
,
});
}
/**
* 删除事件
*/
async
function
handleDelete
(
record
)
{
await
deleteOne
({
id
:
record
.
id
},
handleSuccess
);
}
/**
* 批量删除事件
*/
async
function
batchHandleDelete
()
{
await
batchDelete
({
ids
:
selectedRowKeys
.
value
},
handleSuccess
);
}
/**
* 成功回调
*/
function
handleSuccess
()
{
(
selectedRowKeys
.
value
=
[])
&&
reload
();
}
/**
* 操作栏
*/
function
handleShowHistory
(
record
)
{
drawerHistoryVisible
.
value
=
true
;
deployId
.
value
=
record
.
deployId
;
dataId
.
value
=
record
.
id
;
refFlowHistory
.
value
.
iniData
(
record
);
}
function
getTableAction
(
record
){
return
[
{
label
:
'编辑'
,
onClick
:
handleEdit
.
bind
(
null
,
record
),
auth
:
'problem:st_problem_check_archive:edit'
},
{
label
:
'查看流程'
,
onClick
:
handleShowHistory
.
bind
(
null
,
record
),
}
]
}
/**
* 下拉操作栏
*/
function
getDropDownAction
(
record
){
let
dropDownAction
=
[
{
...
...
@@ -191,48 +183,12 @@
},
auth
:
'problem:st_problem_check_archive:delete'
},
{
label
:
'审批进度'
,
onClick
:
handlePreviewPic
.
bind
(
null
,
record
),
ifShow
:
!!
record
.
bpmStatus
&&
record
.
bpmStatus
!==
'1'
,
}
];
if
(
record
.
bpmStatus
==
'1'
){
dropDownAction
.
push
({
label
:
'发起流程'
,
popConfirm
:
{
title
:
'确认提交流程吗?'
,
confirm
:
handleProcess
.
bind
(
null
,
record
),
placement
:
'topLeft'
,
}
})
}
return
dropDownAction
;
}
/**
* 提交流程
*/
async
function
handleProcess
(
record
)
{
let
params
=
{
flowCode
:
'dev_st_problem_check_archive_001'
,
id
:
record
.
id
,
formUrl
:
'problem/components/StProblemCheckArchiveForm'
,
formUrlMobile
:
''
}
await
startProcess
(
params
);
handleSuccess
();
}
/**
* 审批进度
*/
async
function
handlePreviewPic
(
record
)
{
bpmPicModal
(
true
,
{
flowCode
:
'dev_st_problem_check_archive_001'
,
dataId
:
record
.
id
,
});
}
</
script
>
...
...
zrch-risk-client-39/src/views/project/problemCheck/StProblemArchiveList.vue
浏览文件 @
62b97b82
...
...
@@ -12,7 +12,7 @@
</BasicTable>
<!-- 整改执行 -->
<StProblemCheckExecuteModal
@
register=
"registerExecuteModal"
@
success=
"handleSuccess"
/>
</div>
</template>
...
...
@@ -25,7 +25,7 @@
import
{
list
,
problemArchive
}
from
'./StProblemCheck.api'
;
import
StProblemCheckExecuteModal
from
'./components/StProblemCheckExecuteModal.vue'
;
const
emit
=
defineEmits
([
'callback'
])
const
emit
=
defineEmits
([
'callback'
,
'openMultiForm'
])
const
props
=
defineProps
({
beforeFlowNode
:
{
...
...
zrch-risk-client-39/src/views/project/problemCheck/StProblemCheck.data.ts
浏览文件 @
62b97b82
...
...
@@ -187,10 +187,9 @@ export const formSchema: FormSchema[] = [
component
:
'JEditor'
,
required
:
true
,
componentProps
:
{
rows
:
4
,
showCount
:
true
,
maxlength
:
30000
,
height
:
30
0
,
maxlength
:
512
,
height
:
22
0
,
},
},
{
...
...
zrch-risk-client-39/src/views/project/problemCheck/StProblemCheckList.vue
浏览文件 @
62b97b82
...
...
@@ -52,7 +52,7 @@
}
})
const
emit
=
defineEmits
([
'callback'
,
'startWorkFlow'
,
'sendWorkFlow'
])
const
emit
=
defineEmits
([
'callback'
,
'startWorkFlow'
,
'sendWorkFlow'
,
'openMultiForm'
])
//注册model
const
[
registerModal
,
{
openModal
}]
=
useModal
();
...
...
@@ -182,8 +182,21 @@
})
}
async
function
handleStartUpdate
(
flowData
)
{
let
record
=
{
procInsId
:
flowData
.
procInsId
,
id
:
flowData
.
dataId
}
await
saveOrUpdate
(
record
,
true
).
then
(
res
=>
{
handleSuccess
(
null
);
})
}
defineExpose
({
handleUpdate
,
handleStartUpdate
})
</
script
>
...
...
zrch-risk-client-39/src/views/project/problemCheck/StProblemExecApprovalList.vue
浏览文件 @
62b97b82
...
...
@@ -25,7 +25,7 @@
import
{
list
,
saveOrUpdate
}
from
'./StProblemCheck.api'
;
import
StProblemCheckExecuteModal
from
'./components/StProblemCheckExecuteModal.vue'
;
const
emit
=
defineEmits
([
'callback'
,
'sendWorkFlow'
])
const
emit
=
defineEmits
([
'callback'
,
'sendWorkFlow'
,
'openMultiForm'
])
const
props
=
defineProps
({
beforeFlowNode
:
{
...
...
@@ -64,7 +64,7 @@
const
[
registerTable
,
{
reload
},
{
rowSelection
,
selectedRowKeys
}]
=
tableContext
;
function
handleExecuteApproval
(
record
:
Recordable
)
{
emit
(
"
callback
"
,
record
)
emit
(
"
openMultiForm
"
,
record
)
}
function
handleSuccess
()
{
...
...
zrch-risk-client-39/src/views/project/problemCheck/StProblemExecList.vue
浏览文件 @
62b97b82
...
...
@@ -25,7 +25,7 @@
import
{
list
,
saveOrUpdate
}
from
'./StProblemCheck.api'
;
import
StProblemCheckExecuteModal
from
'./components/StProblemCheckExecuteModal.vue'
;
const
emit
=
defineEmits
([
'callback'
,
'sendWorkFlow'
])
const
emit
=
defineEmits
([
'callback'
,
'sendWorkFlow'
,
'openMultiForm'
])
const
props
=
defineProps
({
beforeFlowNode
:
{
...
...
@@ -64,7 +64,7 @@
const
[
registerTable
,
{
reload
},
{
rowSelection
,
selectedRowKeys
}]
=
tableContext
;
function
handleExecute
(
record
:
Recordable
)
{
emit
(
"
callback
"
,
record
)
emit
(
"
openMultiForm
"
,
record
)
}
function
handleSuccess
()
{
...
...
zrch-risk-client-39/src/views/project/problemCheck/StProblemIndex.vue
浏览文件 @
62b97b82
...
...
@@ -11,16 +11,12 @@
:currentFlowNode=
"node"
:nextFlowNode=
"workflowNodes[index+1]"
@
open-multi-form=
"handleOpenMultiForm"
@
callback=
"handleCallback"
/>
</div>
<div
v-else
class=
"no-form"
>
该节点未配置表单
</div>
</a-tab-pane>
<template
#
tabBarExtraContent
>
<a-button
type=
"primary"
>
查看流程图
</a-button>
</
template
>
</a-tabs>
<WorkFlowFormDrawer
v-model:visible=
"drawerVisible"
...
...
@@ -29,34 +25,29 @@
:workflow-nodes=
"workflowNodes"
:external-form-data=
"externalFormData"
:proc-def-id=
"currentProcDefId"
:proc-ins-id=
"procInsId"
:show-history-form-data=
"true"
:data-id=
"dataId"
:deploy-id=
"deployId"
:show-approval-panel=
"isShowApprovalPanel"
@
submit=
"handleMultiFormSubmit"
@
close=
"handleDrawerClose"
@
form-data-update=
"handleMultiFormDataUpdate"
width=
"90%"
/>
<TaskAssigneeDrawer
v-model:visible=
"drawerTaskVisible"
:task-id=
"taskId"
:task-name=
"taskName"
:proc-def-id=
"procDefId"
:proc-def-name=
"procDefName"
:show-task-info=
"true"
:show-form-data=
"true"
:assignee-title=
"assigneeTitle"
:user-type=
"userType"
:assignee=
"assignee"
:deploy-id=
"deployId"
:user-type-options=
"userTypeOptions"
:custom-next-api=
"customNextApi"
:form-data=
"formData"
:data-id=
"dataId"
@
success=
"handlSendSuccess"
@
error=
"handleError"
/>
</div>
</
template
>
...
...
@@ -66,8 +57,8 @@
import
{
definitionStart
,
definitionStartByDeployId
,
addMyTaskFlow
}
from
"/@/components/Process/api/definition"
;
import
WorkFlowFormDrawer
from
'/@/views/common/WorkFlowFormDrawer.vue'
;
import
TaskAssigneeDrawer
from
'/@/views/common/TaskAssigneeDrawer.vue'
import
{
CONNREFUSED
}
from
'dns'
;
const
formTableName
=
"st_problem_check"
;
const
workflowNodes
=
ref
<
any
[]
>
([]);
const
activeTab
=
ref
(
1
);
...
...
@@ -75,33 +66,23 @@
const
currentNode
=
ref
<
any
>
({});
const
isShowApprovalPanel
=
ref
(
true
);
const
deployId
=
ref
(
''
);
const
procInsId
=
ref
(
''
);
// 改为动态 ref 对象,存储每个节点的组件实例
const
formComponentRefs
=
ref
<
Map
<
number
,
any
>>
(
new
Map
());
// 任务指派抽屉相关状态
const
drawerTaskVisible
=
ref
(
false
);
const
taskId
=
ref
(
''
)
const
taskName
=
ref
(
''
)
const
procDefId
=
ref
(
''
)
const
procDefName
=
ref
(
''
)
const
assignee
=
ref
<
any
>
(
""
);
const
assigneeTitle
=
ref
<
any
>
(
""
);
const
userType
=
ref
<
any
>
(
""
);
const
formData
=
ref
<
any
>
({});
const
userTypeOptions
=
ref
<
any
>
([
{
value
:
'user'
,
label
:
'用户'
},
{
value
:
'role'
,
label
:
'角色'
}
]);
const
formData
=
ref
<
any
>
({});
const
customNextApi
=
ref
<
any
>
(
null
);
// 抽屉相关状态
const
drawerVisible
=
ref
(
false
);
const
drawerTitle
=
ref
(
'表单处理'
);
const
currentMultiFormIndex
=
ref
(
0
);
const
currentProcDefId
=
ref
(
''
);
const
externalFormData
=
ref
<
Record
<
string
,
any
>>
({});
const
componentCache
=
new
Map
();
const
modules
=
import
.
meta
.
glob
(
'@/views/**/*.vue'
);
...
...
@@ -127,10 +108,7 @@
}
function
loadComponent
(
url
:
string
)
{
console
.
log
(
'开始加载组件,URL:'
,
url
);
if
(
componentCache
.
has
(
url
))
{
console
.
log
(
'从缓存加载组件:'
,
url
);
return
componentCache
.
get
(
url
);
}
let
componentPath
=
''
;
...
...
@@ -142,13 +120,9 @@
if
(
!
componentPath
.
match
(
/
\.(
vue|js|ts|jsx|tsx
)
$/
))
{
componentPath
+=
'.vue'
;
}
console
.
log
(
'生成的组件路径:'
,
componentPath
);
console
.
log
(
'所有可用的组件路径示例:'
,
Object
.
keys
(
modules
).
slice
(
0
,
10
));
let
loader
=
modules
[
componentPath
];
if
(
!
loader
)
{
console
.
error
(
'未找到组件:'
,
componentPath
);
console
.
log
(
'包含 problemCheck 的路径:'
,
Object
.
keys
(
modules
).
filter
(
key
=>
key
.
includes
(
'problemCheck'
)));
const
ErrorComponent
=
{
render
:
()
=>
h
(
'div'
,
{
style
:
'color: red; padding: 20px;'
},
`组件未找到:
${
componentPath
}
`
)
};
...
...
@@ -175,17 +149,18 @@
}
const
handleDefinitionStart
=
async
(
data
)
=>
{
const
formData
=
{
dataId
:
data
.
id
,
dataName
:
'id'
};
const
deployId
=
currentNode
.
value
.
deployId
||
''
;
const
startResRaw
=
await
definitionStartByDeployId
(
deployId
,
formData
);
alert
(
JSON
.
stringify
(
startResRaw
))
let
myTaskFlow
=
{}
if
(
startResRaw
?.
procInsId
)
{
procInsId
.
value
=
startResRaw
.
procInsId
;
myTaskFlow
[
"taskId"
]
=
startResRaw
.
taskId
;
myTaskFlow
[
"deployId"
]
=
startResRaw
.
deployId
;
myTaskFlow
[
"procInsId"
]
=
startResRaw
.
instance
Id
;
myTaskFlow
[
"procInsId"
]
=
startResRaw
.
procIns
Id
;
myTaskFlow
[
"executionId"
]
=
startResRaw
.
executionId
;
myTaskFlow
[
"procDefId"
]
=
startResRaw
.
procInsId
;
myTaskFlow
[
"procDefId"
]
=
startResRaw
.
instanceId
;
myTaskFlow
[
"targetId"
]
=
data
.
id
;
myTaskFlow
[
"taskDefinitionKey"
]
=
currentNode
.
value
.
id
;
myTaskFlow
[
"formTableName"
]
=
formTableName
;
...
...
@@ -200,77 +175,88 @@
}
}
await
addMyTaskFlow
(
myTaskFlow
);
}
const
currentFormComponent
=
getCurrentFormComponent
();
if
(
currentFormComponent
&&
typeof
currentFormComponent
.
handleUpdate
===
'function'
)
{
currentFormComponent
.
handleStartUpdate
({
dataId
:
data
.
id
,
procInsId
:
startResRaw
.
procInsId
,
});
}
else
{
console
.
warn
(
'当前组件实例不存在或没有 handleUpdate 方法'
);
}
}
}
const
handleDefinitionSend
=
async
(
data
)
=>
{
drawerTaskVisible
.
value
=
true
;
const
nextNode
=
workflowNodes
.
value
[
activeTab
.
value
];
if
(
nextNode
?.
assignee
)
{
assignee
.
value
=
nextNode
.
assignee
;
}
else
{
assignee
.
value
=
''
;
}
dataId
.
value
=
data
.
id
;
deployId
.
value
=
currentNode
.
value
.
deployId
||
''
;
await
setNextNodeUser
();
}
deployId
.
value
=
nextNode
?.
deployId
||
''
;
const
attributes
=
nextNode
.
value
?.
attributes
||
{};
const
setNextNodeUser
=
async
()
=>
{
const
nextNode
=
workflowNodes
.
value
[
activeTab
.
value
];
const
attributes
=
nextNode
.
attributes
||
{};
const
userTypes
=
attributes
.
userType
||
[];
if
(
userTypes
.
length
>
0
)
{
userType
.
value
=
userTypes
[
0
].
value
||
''
;
if
(
userType
.
value
===
"role"
){
if
(
nextNode
.
candidateGroups
){
assignee
.
value
=
nextNode
.
candidateGroups
[
0
].
id
;
}
}
else
{
assignee
.
value
=
nextNode
.
assignee
;
}
}
}
formData
.
value
=
data
;
}
const
handleOpenMultiForm
=
(
params
:
{
nodeIndex
?:
number
;
title
?:
string
;
procDefId
?:
string
;
formData
?:
Record
<
string
,
any
>
;
})
=>
{
//
const handleOpenMultiForm = (params: {
//
nodeIndex?: number;
//
title?: string;
//
procDefId?: string;
//
formData?: Record
<
string
,
any
>
;
//
}) => {
if
(
params
.
nodeIndex
!==
undefined
)
{
currentMultiFormIndex
.
value
=
params
.
nodeIndex
;
}
else
{
currentMultiFormIndex
.
value
=
activeTab
.
value
-
1
;
}
if
(
params
.
title
)
{
drawerTitle
.
value
=
params
.
title
;
}
else
{
const
currentNode
=
workflowNodes
.
value
[
currentMultiFormIndex
.
value
];
drawerTitle
.
value
=
currentNode
?
`
${
currentNode
.
name
}
- 历史表单查看`
:
'表单处理'
;
}
if
(
params
.
procDefId
)
{
currentProcDefId
.
value
=
params
.
procDefId
;
}
else
if
(
workflowNodes
.
value
[
currentMultiFormIndex
.
value
])
{
currentProcDefId
.
value
=
workflowNodes
.
value
[
currentMultiFormIndex
.
value
].
procDefId
||
''
;
}
if
(
params
.
formData
)
{
externalFormData
.
value
=
params
.
formData
;
}
else
{
externalFormData
.
value
=
{};
}
drawerVisible
.
value
=
true
;
};
// if (params.nodeIndex !== undefined) {
// currentMultiFormIndex.value = params.nodeIndex;
// } else {
// currentMultiFormIndex.value = activeTab.value - 1;
// }
// if (params.title) {
// drawerTitle.value = params.title;
// } else {
// const currentNode = workflowNodes.value[currentMultiFormIndex.value];
// drawerTitle.value = currentNode ? `${currentNode.name} - 历史表单查看` : '表单处理';
// }
// if (params.procDefId) {
// currentProcDefId.value = params.procDefId;
// } else if (workflowNodes.value[currentMultiFormIndex.value]) {
// currentProcDefId.value = workflowNodes.value[currentMultiFormIndex.value].procDefId || '';
// }
// if (params.formData) {
// externalFormData.value = params.formData;
// } else {
// externalFormData.value = {};
// }
// drawerVisible.value = true;
// };
const
handleMultiFormSubmit
=
async
(
submitData
:
{
nodeId
:
string
;
nodeName
:
string
;
formData
:
any
;
procDefId
:
string
;
nodeId
:
string
;
nodeName
:
string
;
formData
:
any
;
procDefId
:
string
;
})
=>
{
console
.
log
(
'多表单提交数据:'
,
submitData
);
try
{
await
definitionStart
(
currentProcDefId
.
value
,
{
currentProcDefId
.
value
,{
...
submitData
.
formData
}
);
drawerVisible
.
value
=
false
;
const
currentTabKey
=
activeTab
.
value
;
const
currentComponent
=
loadComponent
(
workflowNodes
.
value
[
currentTabKey
-
1
]?.
formListUrl
);
const
currentComponent
=
loadComponent
(
workflowNodes
.
value
[
activeTab
.
value
-
1
]?.
formListUrl
);
}
catch
(
error
)
{
console
.
error
(
'提交失败:'
,
error
);
throw
error
;
...
...
@@ -286,17 +272,6 @@
// 可以在这里实时保存数据到本地
};
const
openHistoryForms
=
(
nodeIndex
:
number
,
formData
?:
Record
<
string
,
any
>
)
=>
{
handleOpenMultiForm
({
nodeIndex
,
title
:
`查看历史表单 -
${
workflowNodes
.
value
[
nodeIndex
]?.
name
||
''
}
`,
formData
});
};
function handleSuccess(response: any) {
console.log('任务处理成功:', response)
}
function
handlSendSuccess
(
dataId
:
any
)
{
const
currentFormComponent
=
getCurrentFormComponent
();
...
...
@@ -311,28 +286,33 @@
console
.
error
(
'任务处理失败:'
,
error
)
}
defineExpose({
openHistoryForms,
openMultiForm: handleOpenMultiForm
});
onMounted(async () => {
async
function
handleOpenMultiForm
(
data
:
any
)
{
deployId
.
value
=
currentNode
.
value
.
deployId
||
''
;
procInsId
.
value
=
data
.
procInsId
||
''
;
drawerVisible
.
value
=
true
;
dataId
.
value
=
data
.
id
||
''
;
currentNode
.
value
=
workflowNodes
.
value
[
currentMultiFormIndex
.
value
];
currentProcDefId
.
value
=
currentNode
.
value
.
procDefId
||
''
;
const
isApprovalNode
=
JSON
.
parse
(
currentNode
.
value
[
"isApprove"
]
||
'false'
);
if
(
isApprovalNode
)
{
isShowApprovalPanel
.
value
=
true
;
}
else
{
isShowApprovalPanel
.
value
=
false
;
}
await
setNextNodeUser
();
}
onMounted
(
async
()
=>
{
await
nextTick
();
try
{
const
nodes
=
await
getNodesByTableName
(
formTableName
);
workflowNodes
.
value
=
nodes
;
console.log('获取到的节点:', workflowNodes.value);
workflowNodes.value.forEach((node, index) => {
console.log(`
节点
$
{
index
+
1
}:
`, node.name, 'formListUrl:', node.formListUrl);
});
if
(
workflowNodes
.
value
&&
workflowNodes
.
value
.
length
>
0
)
{
console.log('开始预加载组件...');
workflowNodes
.
value
.
forEach
(
node
=>
{
if
(
node
.
formListUrl
)
{
console.log('预加载组件 URL:', node.formListUrl);
loadComponent
(
node
.
formListUrl
);
}
});
...
...
@@ -343,21 +323,7 @@
}
});
function handleCallback(data: any) {
drawerVisible.value = true;
dataId.value = data.id || '';
currentNode.value = workflowNodes.value[currentMultiFormIndex.value];
currentProcDefId.value = currentNode.value.procDefId || '';
const isApprovalNode = JSON.parse(currentNode.value["isApprove"] || 'false');
if(isApprovalNode) {
isShowApprovalPanel.value = true;
} else {
isShowApprovalPanel.value = false;
}
}
</
script
>
<
style
scoped
>
...
...
zrch-risk-client-39/src/views/project/problemCheck/StProblemPlanApprovalList.vue
浏览文件 @
62b97b82
...
...
@@ -40,7 +40,7 @@
}
})
const
emit
=
defineEmits
([
'callback'
,
'sendWorkFlow'
])
const
emit
=
defineEmits
([
'callback'
,
'sendWorkFlow'
,
'openMultiForm'
])
//注册model
const
[
registerExecuteModal
,
{
openModal
:
openExecuteModal
}]
=
useModal
();
...
...
@@ -64,7 +64,7 @@
const
[
registerTable
,
{
reload
},
{
rowSelection
,
selectedRowKeys
}]
=
tableContext
;
function
handlePlanApproval
(
record
:
Recordable
)
{
emit
(
"
callback
"
,
record
)
emit
(
"
openMultiForm
"
,
record
)
}
function
handleSuccess
()
{
...
...
zrch-risk-client-39/src/views/project/problemCheck/StProblemPlanList.vue
浏览文件 @
62b97b82
...
...
@@ -25,7 +25,7 @@
import
{
list
,
saveOrUpdate
}
from
'./StProblemCheck.api'
;
import
StProblemCheckExecuteModal
from
'./components/StProblemCheckExecuteModal.vue'
;
const
emit
=
defineEmits
([
'callback'
,
'sendWorkFlow'
])
const
emit
=
defineEmits
([
'callback'
,
'sendWorkFlow'
,
'openMultiForm'
])
const
props
=
defineProps
({
beforeFlowNode
:
{
...
...
@@ -62,7 +62,7 @@
const
[
registerTable
,
{
reload
},
{
rowSelection
,
selectedRowKeys
}]
=
tableContext
;
function
handlePlan
(
record
:
Recordable
)
{
emit
(
"
callback
"
,
record
)
emit
(
"
openMultiForm
"
,
record
)
}
async
function
handleSendNext
(
record
:
Recordable
)
{
...
...
@@ -87,7 +87,6 @@
}
async
function
handleUpdate
(
dataId
)
{
//alert(dataId)
let
record
=
{
bmpNodeId
:
props
.
nextFlowNode
.
id
,
deployId
:
props
.
nextFlowNode
.
deployId
,
...
...
zrch-risk-client-39/src/views/project/problemCheck/components/StProblemCheckExecuteForm.vue
浏览文件 @
62b97b82
...
...
@@ -14,6 +14,7 @@
import
{
propTypes
}
from
'/@/utils/propTypes'
;
import
{
getExecuteFormSchema
}
from
'../StProblemCheck.data'
;
import
{
saveOrUpdate
,
}
from
'../StProblemCheck.api'
;
import
{
useMessage
}
from
'/@/hooks/web/useMessage'
;
export
default
defineComponent
({
name
:
'StProblemCheckForm'
,
...
...
@@ -27,7 +28,8 @@
disabled
:
propTypes
.
bool
.
def
(
false
),
},
setup
(
props
)
{
const
[
registerForm
,
{
setFieldsValue
,
setProps
,
getFieldsValue
}]
=
useForm
({
const
{
createMessage
}
=
useMessage
();
const
[
registerForm
,
{
validate
,
setFieldsValue
,
setProps
,
getFieldsValue
}]
=
useForm
({
labelWidth
:
150
,
schemas
:
getExecuteFormSchema
(
props
.
formData
),
showActionButtonGroup
:
false
,
...
...
@@ -53,7 +55,16 @@
async
function
submitForm
()
{
let
data
=
getFieldsValue
();
let
params
=
Object
.
assign
({},
formData
,
data
);
await
saveOrUpdate
(
params
,
true
);
const
result
=
await
saveOrUpdate
(
params
,
true
);
if
(
result
&&
result
.
id
)
{
await
initFormData
(
result
.
id
);
createMessage
.
success
(
'保存成功!'
);
//emit('save-success', result);
}
else
{
await
initFormData
(
props
.
dataId
);
createMessage
.
success
(
'保存成功!'
);
}
}
initFormData
(
props
.
dataId
);
...
...
zrch-risk-client-39/src/views/project/problemCheck/components/StProblemCheckForm.vue
浏览文件 @
62b97b82
...
...
@@ -28,7 +28,7 @@
},
setup
(
props
)
{
const
[
registerForm
,
{
setFieldsValue
,
setProps
,
getFieldsValue
}]
=
useForm
({
labelWidth
:
1
5
0
,
labelWidth
:
1
0
0
,
schemas
:
getBpmFormSchema
(
props
.
formData
),
showActionButtonGroup
:
false
,
baseColProps
:
{
span
:
24
},
...
...
zrch-risk-client-39/src/views/project/problemCheck/components/StProblemCheckPlanForm.vue
浏览文件 @
62b97b82
<
template
>
<div
style=
"min-height: 400px"
>
<BasicForm
@
register=
"registerForm"
/>
<div
style=
"width: 100%; text-align: center"
v-if=
"!formDisabled"
>
<a-button
@
click=
"submitForm"
pre-icon=
"ant-design:check"
type=
"primary"
>
提 交
</a-button>
</div>
<div
style=
"height: 100%; display: flex; flex-direction: column"
>
<a-card
:style=
"
{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'auto' }" :bordered="false" :body-style="{ flex: 1, display: 'flex', flexDirection: 'column' }">
<!-- 表单内容区域 -->
<div
style=
"flex: 1; overflow: auto"
>
<BasicForm
@
register=
"registerForm"
/>
</div>
<!-- 底部按钮区域 -->
<div
style=
"text-align: center;"
v-if=
"!formDisabled"
>
<a-space>
<a-button
block
@
click=
"saveForm"
pre-icon=
"ant-design:save"
type=
"primary"
ghost
>
保 存
</a-button>
<a-button
block
@
click=
"saveAndSendForm"
pre-icon=
"ant-design:send"
type=
"primary"
ghost
>
保存并发送
</a-button>
</a-space>
</div>
</a-card>
</div>
</
template
>
<
script
lang=
"ts"
>
import
{
BasicForm
,
useForm
}
from
'/@/components/Form/index'
;
import
{
computed
,
defineComponent
}
from
'vue'
;
import
{
computed
,
defineComponent
,
ref
}
from
'vue'
;
import
{
defHttp
}
from
'/@/utils/http/axios'
;
import
{
propTypes
}
from
'/@/utils/propTypes'
;
import
{
getPlanFormSchema
}
from
'../StProblemCheck.data'
;
import
{
saveOrUpdate
,
}
from
'../StProblemCheck.api'
;
import
{
saveOrUpdate
}
from
'../StProblemCheck.api'
;
import
{
useMessage
}
from
'/@/hooks/web/useMessage'
;
export
default
defineComponent
({
name
:
'StProblemCheckForm'
,
...
...
@@ -26,9 +37,11 @@
dataId
:
propTypes
.
string
.
def
(
''
),
disabled
:
propTypes
.
bool
.
def
(
false
),
},
setup
(
props
)
{
emits
:
[
'save-success'
,
'send-success'
],
setup
(
props
,
{
emit
})
{
const
{
createMessage
}
=
useMessage
();
const
[
registerForm
,
{
setFieldsValue
,
setProps
,
getFieldsValue
}]
=
useForm
({
labelWidth
:
1
5
0
,
labelWidth
:
1
0
0
,
schemas
:
getPlanFormSchema
(
props
.
formData
),
showActionButtonGroup
:
false
,
baseColProps
:
{
span
:
24
},
...
...
@@ -41,32 +54,94 @@
return
true
;
});
let
formData
=
{}
;
let
currentFormData
=
ref
({})
;
const
queryById
=
'/problem/stProblemCheck/queryById'
;
async
function
initFormData
(
did
)
{
let
params
=
{
id
:
props
.
dataId
||
did
};
let
params
=
{
id
:
props
.
dataId
||
did
};
const
data
=
await
defHttp
.
get
({
url
:
queryById
,
params
});
formData
=
{
...
data
};
await
setFieldsValue
(
formData
);
currentFormData
.
value
=
{
...
data
};
await
setFieldsValue
(
currentFormData
.
value
);
await
setProps
({
disabled
:
formDisabled
.
value
});
}
async
function
submitForm
()
{
let
data
=
getFieldsValue
();
let
params
=
Object
.
assign
({},
formData
,
data
);
await
saveOrUpdate
(
params
,
true
);
alert
(
1
)
// 保存按钮
async
function
saveForm
()
{
try
{
let
data
=
getFieldsValue
();
let
params
=
Object
.
assign
({},
currentFormData
.
value
,
data
);
const
result
=
await
saveOrUpdate
(
params
,
true
);
// 保存成功后更新表单
if
(
result
&&
result
.
id
)
{
await
initFormData
(
result
.
id
);
createMessage
.
success
(
'保存成功!'
);
emit
(
'save-success'
,
result
);
}
else
{
await
initFormData
(
props
.
dataId
);
createMessage
.
success
(
'保存成功!'
);
}
}
catch
(
error
)
{
createMessage
.
error
(
'保存失败,请重试!'
);
console
.
error
(
'保存失败:'
,
error
);
}
}
// 保存并发送按钮
async
function
saveAndSendForm
()
{
try
{
let
data
=
getFieldsValue
();
let
params
=
Object
.
assign
({},
currentFormData
.
value
,
data
);
const
result
=
await
saveOrUpdate
(
params
,
true
);
// 保存成功后更新表单
if
(
result
&&
result
.
id
)
{
await
initFormData
(
result
.
id
);
createMessage
.
success
(
'保存成功!'
);
// TODO: 调用发送接口
// await sendData(result.id);
createMessage
.
success
(
'发送成功!'
);
emit
(
'send-success'
,
result
);
}
else
{
await
initFormData
(
props
.
dataId
);
createMessage
.
success
(
'保存成功!'
);
// TODO: 调用发送接口
createMessage
.
success
(
'发送成功!'
);
}
}
catch
(
error
)
{
createMessage
.
error
(
'保存失败,请重试!'
);
console
.
error
(
'保存失败:'
,
error
);
}
}
initFormData
(
props
.
dataId
);
const
setFormDisabledStatus
=
(
isEdit
:
boolean
)
=>
{
if
(
props
.
disabled
===
false
)
{
return
false
;
}
return
true
;
};
return
{
setFormDisabledStatus
,
registerForm
,
formDisabled
,
submitForm
,
saveForm
,
saveAndSendForm
,
initFormData
};
},
});
</
script
>
<
style
scoped
>
/* 确保父容器高度100% */
:deep
(
.ant-card-body
)
{
flex
:
1
;
display
:
flex
;
flex-direction
:
column
;
padding
:
24px
;
}
</
style
>
\ No newline at end of file
zrch-risk-server-39/jeecg-module-system/jeecg-module-flowable/src/main/java/org/jeecg/modules/flowable/controller/FlowFormController.java
浏览文件 @
62b97b82
...
...
@@ -16,6 +16,7 @@ import org.jeecg.modules.flowable.apithird.business.entity.FlowForm;
import
org.jeecg.modules.flowable.apithird.business.entity.SysDeployForm
;
import
org.jeecg.modules.flowable.apithird.business.service.IFlowFormService
;
import
org.jeecg.modules.flowable.apithird.business.service.ISysDeployFormService
;
import
org.jeecg.modules.flowable.service.IFlowRejectService
;
import
org.jeecg.modules.stm.my.entity.MyTaskFlow
;
import
org.jeecg.modules.stm.my.service.IMyTaskFlowService
;
import
org.jeecg.modules.stm.utils.UserUtil
;
...
...
@@ -23,25 +24,23 @@ import org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.web.bind.annotation.*
;
import
java.util.*
;
import
java.util.stream.Collectors
;
import
org.flowable.task.api.Task
;
@Slf4j
@RestController
@RequestMapping
(
"/flowable/form"
)
public
class
FlowFormController
{
@Autowired
private
IFlowFormService
flowFormService
;
@Autowired
private
ISysDeployFormService
sysDeployFormService
;
@Autowired
private
RepositoryService
repositoryService
;
@Autowired
private
IMyTaskFlowService
myTaskFlowService
;
@Autowired
private
IFlowRejectService
flowRejectService
;
@RequestMapping
(
"/list"
)
public
Result
<
IPage
<
FlowForm
>>
getFormList
(
...
...
@@ -237,6 +236,9 @@ public class FlowFormController {
nodeInfo
.
put
(
"isApprove"
,
isApprove
);
String
formKey
=
((
UserTask
)
element
).
getFormKey
();
nodeInfo
.
put
(
"assignee"
,
userTask
.
getAssignee
());
nodeInfo
.
put
(
"candidateGroups"
,
userTask
.
getCandidateGroups
());
if
(
formKey
!=
null
)
{
try
{
Long
formId
=
Long
.
parseLong
(
formKey
);
...
...
@@ -394,4 +396,106 @@ public class FlowFormController {
return
extensionProperties
;
}
/**
* 根据当前任务节点,判断审批不通过时走向哪个节点
* @param taskId 当前任务节点ID(如 "userTask1")
* @param deployId 部署ID
* @param isApproved 是否通过(true=通过,false=不通过)
* @return 目标节点信息
*/
@GetMapping
(
"/appReject"
)
public
void
appReject
(
@RequestParam
String
taskId
,
@RequestParam
String
deployId
,
@RequestParam
String
currentTaskId
,
@RequestParam
String
rejectReason
,
@RequestParam
String
targetTaskId
,
@RequestParam
boolean
isApproved
)
{
Task
task
=
flowRejectService
.
rejectToTarget
(
currentTaskId
,
targetTaskId
,
rejectReason
);
}
@GetMapping
(
"/getRejectTargetNode"
)
public
Result
<
Map
<
String
,
Object
>>
getRejectTargetNode
(
@RequestParam
String
taskId
,
@RequestParam
String
deployId
,
@RequestParam
boolean
isApproved
)
{
try
{
ProcessDefinition
processDefinition
=
repositoryService
.
createProcessDefinitionQuery
()
.
deploymentId
(
deployId
).
singleResult
();
if
(
processDefinition
==
null
)
{
return
Result
.
error
(
"未找到流程定义"
);
}
BpmnModel
bpmnModel
=
repositoryService
.
getBpmnModel
(
processDefinition
.
getId
());
if
(
bpmnModel
==
null
)
{
return
Result
.
error
(
"无法获取BPMN模型"
);
}
// 找到当前节点
FlowElement
currentElement
=
findFlowElementById
(
bpmnModel
,
taskId
);
if
(!(
currentElement
instanceof
UserTask
))
{
return
Result
.
error
(
"当前节点不是用户任务"
);
}
UserTask
userTask
=
(
UserTask
)
currentElement
;
// 遍历所有 outgoing 连线,找到条件匹配的连线
for
(
SequenceFlow
sequenceFlow
:
userTask
.
getOutgoingFlows
())
{
String
conditionExpression
=
sequenceFlow
.
getConditionExpression
();
if
(
conditionExpression
!=
null
&&
!
conditionExpression
.
trim
().
isEmpty
())
{
// 根据条件表达式判断是“通过”还是“不通过”
boolean
matches
=
evaluateCondition
(
conditionExpression
,
isApproved
);
if
(
matches
)
{
// 找到目标节点
FlowElement
targetElement
=
findFlowElementById
(
bpmnModel
,
sequenceFlow
.
getTargetRef
());
Map
<
String
,
Object
>
result
=
new
HashMap
<>();
result
.
put
(
"targetNodeId"
,
targetElement
.
getId
());
result
.
put
(
"targetNodeName"
,
targetElement
.
getName
());
result
.
put
(
"targetNodeType"
,
targetElement
.
getClass
().
getSimpleName
());
result
.
put
(
"conditionExpression"
,
conditionExpression
);
return
Result
.
OK
(
result
);
}
}
}
return
Result
.
error
(
"未找到匹配的连线条件"
);
}
catch
(
Exception
e
)
{
log
.
error
(
"解析拒绝目标节点失败"
,
e
);
return
Result
.
error
(
"解析失败: "
+
e
.
getMessage
());
}
}
private
FlowElement
findFlowElementById
(
BpmnModel
bpmnModel
,
String
elementId
)
{
for
(
org
.
flowable
.
bpmn
.
model
.
Process
process
:
bpmnModel
.
getProcesses
())
{
FlowElement
element
=
process
.
getFlowElement
(
elementId
);
if
(
element
!=
null
)
{
return
element
;
}
}
return
null
;
}
private
boolean
evaluateCondition
(
String
conditionExpression
,
boolean
isApproved
)
{
// 条件表达式示例:${approved == true} 或 ${approved == false}
// 这里简化处理:根据字符串内容判断
if
(
conditionExpression
==
null
)
return
false
;
String
expr
=
conditionExpression
.
trim
();
if
(
isApproved
)
{
// 通过条件:通常包含 true, 1, 'yes', 'pass' 等
return
expr
.
matches
(
".*\\$\\{.*(true|1|'yes'|'pass').*}.*"
);
}
else
{
// 不通过条件:通常包含 false, 0, 'no', 'reject' 等
return
expr
.
matches
(
".*\\$\\{.*(false|0|'no'|'reject').*}.*"
);
}
}
}
zrch-risk-server-39/jeecg-module-system/jeecg-module-flowable/src/main/java/org/jeecg/modules/flowable/service/IFlowRejectService.java
0 → 100644
浏览文件 @
62b97b82
package
org
.
jeecg
.
modules
.
flowable
.
service
;
import
org.flowable.task.api.Task
;
public
interface
IFlowRejectService
{
/**
* 驳回任务到指定历史节点
*
* @param currentTaskId 当前任务ID
* @param targetTaskId 目标历史任务ID(要驳回到的节点)
* @param rejectReason 驳回原因
* @return 新创建的目标节点任务
* @throws RuntimeException 当目标任务不存在、当前任务不存在或驳回到当前节点时抛出
*/
Task
rejectToTarget
(
String
currentTaskId
,
String
targetTaskId
,
String
rejectReason
);
}
\ No newline at end of file
zrch-risk-server-39/jeecg-module-system/jeecg-module-flowable/src/main/java/org/jeecg/modules/flowable/service/impl/FlowDefinitionServiceImpl.java
浏览文件 @
62b97b82
package
org
.
jeecg
.
modules
.
flowable
.
service
.
impl
;
import
cn.hutool.core.collection.CollUtil
;
import
cn.hutool.core.util.StrUtil
;
import
com.alibaba.fastjson.JSON
;
import
com.alibaba.fastjson.JSONArray
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.extension.plugins.pagination.Page
;
import
org.apache.commons.io.IOUtils
;
import
org.flowable.bpmn.model.*
;
import
org.flowable.engine.ProcessEngineConfiguration
;
import
org.flowable.engine.RepositoryService
;
import
org.flowable.engine.history.HistoricActivityInstance
;
import
org.flowable.engine.repository.Deployment
;
import
org.flowable.engine.repository.ProcessDefinition
;
...
...
@@ -19,22 +15,16 @@ import org.flowable.image.ProcessDiagramGenerator;
import
org.flowable.image.impl.DefaultProcessDiagramGenerator
;
import
org.flowable.task.api.Task
;
import
org.jeecg.common.api.vo.Result
;
import
org.jeecg.common.util.SpringContextUtils
;
import
org.jeecg.modules.flowable.apithird.business.entity.FlowForm
;
import
org.jeecg.modules.flowable.apithird.business.entity.FlowMyBusiness
;
import
org.jeecg.modules.flowable.apithird.business.service.ISysDeployFormService
;
import
org.jeecg.modules.flowable.apithird.business.service.impl.FlowMyBusinessServiceImpl
;
import
org.jeecg.modules.flowable.apithird.entity.ActStatus
;
import
org.jeecg.modules.flowable.apithird.entity.SysUser
;
import
org.jeecg.modules.flowable.apithird.service.FlowCallBackServiceI
;
import
org.jeecg.modules.flowable.apithird.service.IFlowThirdService
;
import
org.jeecg.modules.flowable.common.constant.ProcessConstants
;
import
org.jeecg.modules.flowable.common.enums.FlowComment
;
import
org.jeecg.modules.flowable.domain.dto.FlowNextDto
;
import
org.jeecg.modules.flowable.domain.dto.FlowProcDefDto
;
import
org.jeecg.modules.flowable.factory.FlowServiceFactory
;
import
org.jeecg.modules.flowable.service.IFlowDefinitionService
;
import
org.jeecg.modules.stm.my.service.IMyTaskService
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
...
...
@@ -44,8 +34,6 @@ import java.io.IOException;
import
java.io.InputStream
;
import
java.nio.charset.StandardCharsets
;
import
java.util.*
;
import
java.util.stream.Collectors
;
import
org.jeecg.modules.stm.my.entity.MyTask
;
/**
...
...
zrch-risk-server-39/jeecg-module-system/jeecg-module-flowable/src/main/java/org/jeecg/modules/flowable/service/impl/FlowRejectServiceImpl.java
0 → 100644
浏览文件 @
62b97b82
package
org
.
jeecg
.
modules
.
flowable
.
service
.
impl
;
import
org.flowable.engine.HistoryService
;
import
org.flowable.engine.RuntimeService
;
import
org.flowable.engine.TaskService
;
import
org.flowable.task.api.Task
;
import
org.flowable.task.api.history.HistoricTaskInstance
;
import
org.jeecg.modules.flowable.factory.FlowServiceFactory
;
import
org.jeecg.modules.flowable.service.IFlowRejectService
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
java.util.ArrayList
;
import
java.util.List
;
@Service
public
class
FlowRejectServiceImpl
extends
FlowServiceFactory
implements
IFlowRejectService
{
@Autowired
private
RuntimeService
runtimeService
;
@Autowired
private
TaskService
taskService
;
@Autowired
private
HistoryService
historyService
;
/**
* 驳回任务到指定历史节点
* @param currentTaskId 当前任务ID
* @param targetTaskId 目标历史任务ID(要驳回到的节点)
* @param rejectReason 驳回原因
*/
public
Task
rejectToTarget
(
String
currentTaskId
,
String
targetTaskId
,
String
rejectReason
)
{
HistoricTaskInstance
targetTask
=
historyService
.
createHistoricTaskInstanceQuery
()
.
taskId
(
targetTaskId
)
.
singleResult
();
if
(
targetTask
==
null
)
{
throw
new
RuntimeException
(
"驳回目的任务不存在"
);
}
// 2. 获取当前任务
Task
currentTask
=
taskService
.
createTaskQuery
()
.
taskId
(
currentTaskId
)
.
singleResult
();
if
(
currentTask
==
null
)
{
throw
new
RuntimeException
(
"当前任务不存在"
);
}
// 3. 不能驳回到当前节点
if
(
currentTask
.
getTaskDefinitionKey
().
equals
(
targetTask
.
getTaskDefinitionKey
()))
{
throw
new
RuntimeException
(
"不能驳回到当前节点"
);
}
String
processInstanceId
=
currentTask
.
getProcessInstanceId
();
// 4. 添加驳回意见(可选)
taskService
.
addComment
(
currentTaskId
,
processInstanceId
,
"reject"
,
rejectReason
);
// 5. 核心API:回退节点
runtimeService
.
createChangeActivityStateBuilder
()
.
processInstanceId
(
processInstanceId
)
.
moveActivityIdTo
(
currentTask
.
getTaskDefinitionKey
(),
targetTask
.
getTaskDefinitionKey
())
.
changeState
();
// 6. 清理脏数据(中间节点的历史任务和变量)
clearDirtyData
(
processInstanceId
,
currentTask
,
targetTask
);
// 7. 返回新创建的目标节点任务
return
taskService
.
createTaskQuery
()
.
processInstanceId
(
processInstanceId
)
.
taskDefinitionKey
(
targetTask
.
getTaskDefinitionKey
())
.
singleResult
();
}
/**
* 清理驳回节点之间的脏数据
*/
private
void
clearDirtyData
(
String
processInstanceId
,
Task
sourceTask
,
HistoricTaskInstance
targetTask
)
{
// 获取历史任务列表(按开始时间降序)
List
<
HistoricTaskInstance
>
historyTasks
=
historyService
.
createHistoricTaskInstanceQuery
()
.
processInstanceId
(
processInstanceId
)
.
orderByHistoricTaskInstanceStartTime
().
desc
()
.
list
();
// 获取需要删除的任务节点(从目标节点到当前节点之间的所有节点)
List
<
HistoricTaskInstance
>
betweenNodes
=
getBetweenNodes
(
historyTasks
,
sourceTask
.
getId
(),
targetTask
.
getId
());
// 收集需要清理的流程变量Key
List
<
String
>
variableKeys
=
new
ArrayList
<>();
for
(
HistoricTaskInstance
task
:
betweenNodes
)
{
// 根据实际业务,清理相关变量
variableKeys
.
add
(
"approveResult"
);
variableKeys
.
add
(
"approveComment"
);
// 删除历史任务记录
historyService
.
deleteHistoricTaskInstance
(
task
.
getId
());
}
// 删除流程变量
if
(!
variableKeys
.
isEmpty
())
{
runtimeService
.
removeVariables
(
processInstanceId
,
variableKeys
);
}
}
/**
* 获取两个节点之间的所有历史任务
*/
private
List
<
HistoricTaskInstance
>
getBetweenNodes
(
List
<
HistoricTaskInstance
>
historyTasks
,
String
sourceTaskId
,
String
targetTaskId
)
{
List
<
HistoricTaskInstance
>
betweenNodes
=
new
ArrayList
<>();
boolean
startRecord
=
false
;
for
(
HistoricTaskInstance
task
:
historyTasks
)
{
if
(
task
.
getId
().
equals
(
sourceTaskId
))
{
startRecord
=
true
;
}
if
(
startRecord
)
{
betweenNodes
.
add
(
task
);
}
if
(
task
.
getId
().
equals
(
targetTaskId
))
{
break
;
}
}
return
betweenNodes
;
}
}
\ No newline at end of file
zrch-risk-server-39/jeecg-module-system/jeecg-module-stm/src/main/java/org/jeecg/modules/stm/problem/entity/StProblemCheck.java
浏览文件 @
62b97b82
...
...
@@ -134,12 +134,33 @@ public class StProblemCheck implements Serializable {
@JsonFormat
(
timezone
=
"GMT+8"
,
pattern
=
"yyyy-MM-dd"
)
@DateTimeFormat
(
pattern
=
"yyyy-MM-dd"
)
private
java
.
util
.
Date
planEndDate
;
/**整改开始时间*/
@JsonFormat
(
timezone
=
"GMT+8"
,
pattern
=
"yyyy-MM-dd"
)
@DateTimeFormat
(
pattern
=
"yyyy-MM-dd"
)
private
java
.
util
.
Date
execStartDate
;
/**整改结束时间*/
@JsonFormat
(
timezone
=
"GMT+8"
,
pattern
=
"yyyy-MM-dd"
)
@DateTimeFormat
(
pattern
=
"yyyy-MM-dd"
)
private
java
.
util
.
Date
execEndDate
;
/**整蛊执行人ID*/
private
java
.
lang
.
String
execUserId
;
/**执行部门*/
private
java
.
lang
.
String
execDepCode
;
/**整改落实情况*/
private
java
.
lang
.
String
execRemark
;
/**发现人*/
private
java
.
lang
.
String
findUser
;
/**流程状态*/
private
java
.
lang
.
String
bpmStatus
;
/**部署ID*/
private
java
.
lang
.
String
deployId
;
/**流程实例ID*/
private
java
.
lang
.
String
procInsId
;
/**风险等级*/
private
java
.
lang
.
Integer
riskLevel
;
/**流程节点ID*/
...
...
zrch-risk-server-39/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml
浏览文件 @
62b97b82
...
...
@@ -21,16 +21,16 @@ management:
flowable
:
database-schema-update
:
false
cmmn
:
enabled
:
false
app
:
enabled
:
false
content
:
enabled
:
false
dmn
:
enabled
:
false
form
:
enabled
:
false
#
cmmn:
#
enabled: false
#
app:
#
enabled: false
#
content:
#
enabled: false
#
dmn:
#
enabled: false
#
form:
#
enabled: false
spring
:
# main:
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论