feat: 办理人节点

This commit is contained in:
Lesan 2025-02-12 13:57:10 +08:00
parent b0d4e39e68
commit 4ea25f3d5b
6 changed files with 166 additions and 111 deletions

View File

@ -15,6 +15,12 @@
</div> </div>
<div class="handler-item-text">审批人</div> <div class="handler-item-text">审批人</div>
</div> </div>
<div class="handler-item" @click="addNode(NodeType.TRANSACTOR_NODE)">
<div class="approve handler-item-icon">
<span class="iconfont icon-approve icon-size"></span>
</div>
<div class="handler-item-text">办理人</div>
</div>
<div class="handler-item" @click="addNode(NodeType.COPY_TASK_NODE)"> <div class="handler-item" @click="addNode(NodeType.COPY_TASK_NODE)">
<div class="handler-item-icon copy"> <div class="handler-item-icon copy">
<span class="iconfont icon-size icon-copy"></span> <span class="iconfont icon-size icon-copy"></span>
@ -114,13 +120,13 @@ const addNode = (type: number) => {
} }
popoverShow.value = false popoverShow.value = false
if (type === NodeType.USER_TASK_NODE) { if (type === NodeType.USER_TASK_NODE || type === NodeType.TRANSACTOR_NODE) {
const id = 'Activity_' + generateUUID() const id = 'Activity_' + generateUUID()
const data: SimpleFlowNode = { const data: SimpleFlowNode = {
id: id, id: id,
name: NODE_DEFAULT_NAME.get(NodeType.USER_TASK_NODE) as string, name: NODE_DEFAULT_NAME.get(type) as string,
showText: '', showText: '',
type: NodeType.USER_TASK_NODE, type: type,
approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE, approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE,
// //
rejectHandler: { rejectHandler: {

View File

@ -6,7 +6,11 @@
/> />
<!-- 审批节点 --> <!-- 审批节点 -->
<UserTaskNode <UserTaskNode
v-if="currentNode && currentNode.type === NodeType.USER_TASK_NODE" v-if="
currentNode &&
(currentNode.type === NodeType.USER_TASK_NODE ||
currentNode.type === NodeType.TRANSACTOR_NODE)
"
:flow-node="currentNode" :flow-node="currentNode"
@update:flow-node="handleModelValueUpdate" @update:flow-node="handleModelValueUpdate"
@find:parent-node="findFromParentNode" @find:parent-node="findFromParentNode"

View File

@ -23,6 +23,11 @@ export enum NodeType {
*/ */
COPY_TASK_NODE = 12, COPY_TASK_NODE = 12,
/**
*
*/
TRANSACTOR_NODE = 13,
/** /**
* *
*/ */
@ -506,6 +511,7 @@ NODE_DEFAULT_TEXT.set(NodeType.START_USER_NODE, '请设置发起人')
NODE_DEFAULT_TEXT.set(NodeType.DELAY_TIMER_NODE, '请设置延迟器') NODE_DEFAULT_TEXT.set(NodeType.DELAY_TIMER_NODE, '请设置延迟器')
NODE_DEFAULT_TEXT.set(NodeType.ROUTER_BRANCH_NODE, '请设置路由节点') NODE_DEFAULT_TEXT.set(NodeType.ROUTER_BRANCH_NODE, '请设置路由节点')
NODE_DEFAULT_TEXT.set(NodeType.TRIGGER_NODE, '请设置触发器') NODE_DEFAULT_TEXT.set(NodeType.TRIGGER_NODE, '请设置触发器')
NODE_DEFAULT_TEXT.set(NodeType.TRANSACTOR_NODE, '请设置办理人')
export const NODE_DEFAULT_NAME = new Map<number, string>() export const NODE_DEFAULT_NAME = new Map<number, string>()
NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人') NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
@ -515,6 +521,7 @@ NODE_DEFAULT_NAME.set(NodeType.START_USER_NODE, '发起人')
NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器') NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器')
NODE_DEFAULT_NAME.set(NodeType.ROUTER_BRANCH_NODE, '路由分支') NODE_DEFAULT_NAME.set(NodeType.ROUTER_BRANCH_NODE, '路由分支')
NODE_DEFAULT_NAME.set(NodeType.TRIGGER_NODE, '触发器') NODE_DEFAULT_NAME.set(NodeType.TRIGGER_NODE, '触发器')
NODE_DEFAULT_NAME.set(NodeType.TRANSACTOR_NODE, '办理人')
// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序 // 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
export const CANDIDATE_STRATEGY: DictDataVO[] = [ export const CANDIDATE_STRATEGY: DictDataVO[] = [
@ -627,6 +634,16 @@ export const DEFAULT_BUTTON_SETTING: ButtonSetting[] = [
{ id: OperationButtonType.RETURN, displayName: '退回', enable: true } { id: OperationButtonType.RETURN, displayName: '退回', enable: true }
] ]
// 办理人默认的按钮权限设置
export const TRANSACTOR_DEFAULT_BUTTON_SETTING: ButtonSetting[] = [
{ id: OperationButtonType.APPROVE, displayName: '办理', enable: true },
{ id: OperationButtonType.REJECT, displayName: '拒绝', enable: false },
{ id: OperationButtonType.TRANSFER, displayName: '转办', enable: false },
{ id: OperationButtonType.DELEGATE, displayName: '委派', enable: false },
{ id: OperationButtonType.ADD_SIGN, displayName: '加签', enable: false },
{ id: OperationButtonType.RETURN, displayName: '退回', enable: false }
]
// 发起人的按钮权限。暂时定死,不可以编辑 // 发起人的按钮权限。暂时定死,不可以编辑
export const START_USER_BUTTON_SETTING: ButtonSetting[] = [ export const START_USER_BUTTON_SETTING: ButtonSetting[] = [
{ id: OperationButtonType.APPROVE, displayName: '提交', enable: true }, { id: OperationButtonType.APPROVE, displayName: '提交', enable: true },

View File

@ -201,7 +201,7 @@ export function useNodeForm(nodeType: NodeType) {
const deptTreeOptions = inject('deptTree', ref()) // 部门树 const deptTreeOptions = inject('deptTree', ref()) // 部门树
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段 const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
const configForm = ref<UserTaskFormType | CopyTaskFormType>() const configForm = ref<UserTaskFormType | CopyTaskFormType>()
if (nodeType === NodeType.USER_TASK_NODE) { if (nodeType === NodeType.USER_TASK_NODE || nodeType === NodeType.TRANSACTOR_NODE) {
configForm.value = { configForm.value = {
candidateStrategy: CandidateStrategy.USER, candidateStrategy: CandidateStrategy.USER,
approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE, approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE,

View File

@ -25,7 +25,7 @@
<div class="divide-line"></div> <div class="divide-line"></div>
</div> </div>
</template> </template>
<div class="flex flex-items-center mb-3"> <div v-if="currentNode.type === NodeType.USER_TASK_NODE" class="flex flex-items-center mb-3">
<span class="font-size-16px mr-3">审批类型 :</span> <span class="font-size-16px mr-3">审批类型 :</span>
<el-radio-group v-model="approveType"> <el-radio-group v-model="approveType">
<el-radio <el-radio
@ -230,91 +230,100 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-divider content-position="left">审批人拒绝时</el-divider> <div v-if="currentNode.type === NodeType.USER_TASK_NODE">
<el-form-item prop="rejectHandlerType"> <el-divider content-position="left">审批人拒绝时</el-divider>
<el-radio-group v-model="configForm.rejectHandlerType"> <el-form-item prop="rejectHandlerType">
<div class="flex-col"> <el-radio-group v-model="configForm.rejectHandlerType">
<div v-for="(item, index) in REJECT_HANDLER_TYPES" :key="index"> <div class="flex-col">
<el-radio :key="item.value" :value="item.value" :label="item.label" /> <div v-for="(item, index) in REJECT_HANDLER_TYPES" :key="index">
<el-radio :key="item.value" :value="item.value" :label="item.label" />
</div>
</div> </div>
</div> </el-radio-group>
</el-radio-group> </el-form-item>
</el-form-item> <el-form-item
<el-form-item v-if="configForm.rejectHandlerType == RejectHandlerType.RETURN_USER_TASK"
v-if="configForm.rejectHandlerType == RejectHandlerType.RETURN_USER_TASK" label="驳回节点"
label="驳回节点" prop="returnNodeId"
prop="returnNodeId"
>
<el-select filterable v-model="configForm.returnNodeId" clearable style="width: 100%">
<el-option
v-for="item in returnTaskList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-divider content-position="left">审批人超时未处理时</el-divider>
<el-form-item label="启用开关" prop="timeoutHandlerEnable">
<el-switch
v-model="configForm.timeoutHandlerEnable"
active-text="开启"
inactive-text="关闭"
@change="timeoutHandlerChange"
/>
</el-form-item>
<el-form-item
label="执行动作"
prop="timeoutHandlerType"
v-if="configForm.timeoutHandlerEnable"
>
<el-radio-group
v-model="configForm.timeoutHandlerType"
@change="timeoutHandlerTypeChanged"
> >
<el-radio-button <el-select
v-for="item in TIMEOUT_HANDLER_TYPES" filterable
:key="item.value" v-model="configForm.returnNodeId"
:value="item.value" clearable
:label="item.label" style="width: 100%"
/> >
</el-radio-group> <el-option
</el-form-item> v-for="item in returnTaskList"
<el-form-item label="超时时间设置" v-if="configForm.timeoutHandlerEnable"> :key="item.id"
<span class="mr-2">当超过</span> :label="item.name"
<el-form-item prop="timeDuration"> :value="item.id"
<el-input-number />
class="mr-2" </el-select>
:style="{ width: '100px' }" </el-form-item>
v-model="configForm.timeDuration" </div>
:min="1"
controls-position="right" <div v-if="currentNode.type === NodeType.USER_TASK_NODE">
<el-divider content-position="left">审批人超时未处理时</el-divider>
<el-form-item label="启用开关" prop="timeoutHandlerEnable">
<el-switch
v-model="configForm.timeoutHandlerEnable"
active-text="开启"
inactive-text="关闭"
@change="timeoutHandlerChange"
/> />
</el-form-item> </el-form-item>
<el-select <el-form-item
filterable label="执行动作"
v-model="timeUnit" prop="timeoutHandlerType"
class="mr-2" v-if="configForm.timeoutHandlerEnable"
:style="{ width: '100px' }"
@change="timeUnitChange"
> >
<el-option <el-radio-group
v-for="item in TIME_UNIT_TYPES" v-model="configForm.timeoutHandlerType"
:key="item.value" @change="timeoutHandlerTypeChanged"
:label="item.label" >
:value="item.value" <el-radio-button
/> v-for="item in TIMEOUT_HANDLER_TYPES"
</el-select> :key="item.value"
未处理 :value="item.value"
</el-form-item> :label="item.label"
<el-form-item />
label="最大提醒次数" </el-radio-group>
prop="maxRemindCount" </el-form-item>
v-if="configForm.timeoutHandlerEnable && configForm.timeoutHandlerType === 1" <el-form-item label="超时时间设置" v-if="configForm.timeoutHandlerEnable">
> <span class="mr-2">当超过</span>
<el-input-number v-model="configForm.maxRemindCount" :min="1" :max="10" /> <el-form-item prop="timeDuration">
</el-form-item> <el-input-number
class="mr-2"
:style="{ width: '100px' }"
v-model="configForm.timeDuration"
:min="1"
controls-position="right"
/>
</el-form-item>
<el-select
filterable
v-model="timeUnit"
class="mr-2"
:style="{ width: '100px' }"
@change="timeUnitChange"
>
<el-option
v-for="item in TIME_UNIT_TYPES"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
未处理
</el-form-item>
<el-form-item
label="最大提醒次数"
prop="maxRemindCount"
v-if="configForm.timeoutHandlerEnable && configForm.timeoutHandlerType === 1"
>
<el-input-number v-model="configForm.maxRemindCount" :min="1" :max="10" />
</el-form-item>
</div>
<el-divider content-position="left">审批人为空时</el-divider> <el-divider content-position="left">审批人为空时</el-divider>
<el-form-item prop="assignEmptyHandlerType"> <el-form-item prop="assignEmptyHandlerType">
@ -348,30 +357,44 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-divider content-position="left">审批人与提交人为同一人时</el-divider> <div v-if="currentNode.type === NodeType.USER_TASK_NODE">
<el-form-item prop="assignStartUserHandlerType"> <el-divider content-position="left">审批人与提交人为同一人时</el-divider>
<el-radio-group v-model="configForm.assignStartUserHandlerType"> <el-form-item prop="assignStartUserHandlerType">
<div class="flex-col"> <el-radio-group v-model="configForm.assignStartUserHandlerType">
<div v-for="(item, index) in ASSIGN_START_USER_HANDLER_TYPES" :key="index"> <div class="flex-col">
<el-radio :key="item.value" :value="item.value" :label="item.label" /> <div v-for="(item, index) in ASSIGN_START_USER_HANDLER_TYPES" :key="index">
<el-radio :key="item.value" :value="item.value" :label="item.label" />
</div>
</div> </div>
</div> </el-radio-group>
</el-radio-group> </el-form-item>
</el-form-item> </div>
<el-divider content-position="left">是否需要签名</el-divider> <div v-if="currentNode.type === NodeType.USER_TASK_NODE">
<el-form-item prop="signEnable"> <el-divider content-position="left">是否需要签名</el-divider>
<el-switch v-model="configForm.signEnable" active-text="是" inactive-text="否" /> <el-form-item prop="signEnable">
</el-form-item> <el-switch v-model="configForm.signEnable" active-text="是" inactive-text="否" />
</el-form-item>
</div>
<el-divider content-position="left">审批意见</el-divider> <div v-if="currentNode.type === NodeType.USER_TASK_NODE">
<el-form-item prop="reasonRequire"> <el-divider content-position="left">审批意见</el-divider>
<el-switch v-model="configForm.reasonRequire" active-text="必填" inactive-text="非必填" /> <el-form-item prop="reasonRequire">
</el-form-item> <el-switch
v-model="configForm.reasonRequire"
active-text="必填"
inactive-text="非必填"
/>
</el-form-item>
</div>
</el-form> </el-form>
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="操作按钮设置" name="buttons"> <el-tab-pane
label="操作按钮设置"
v-if="currentNode.type === NodeType.USER_TASK_NODE"
name="buttons"
>
<div class="button-setting-pane"> <div class="button-setting-pane">
<div class="button-setting-desc">操作按钮</div> <div class="button-setting-desc">操作按钮</div>
<div class="button-setting-title"> <div class="button-setting-title">
@ -485,7 +508,8 @@ import {
ASSIGN_EMPTY_HANDLER_TYPES, ASSIGN_EMPTY_HANDLER_TYPES,
AssignEmptyHandlerType, AssignEmptyHandlerType,
FieldPermissionType, FieldPermissionType,
ProcessVariableEnum ProcessVariableEnum,
TRANSACTOR_DEFAULT_BUTTON_SETTING
} from '../consts' } from '../consts'
import { import {
@ -588,7 +612,7 @@ const {
handleCandidateParam, handleCandidateParam,
parseCandidateParam, parseCandidateParam,
getShowText getShowText
} = useNodeForm(NodeType.USER_TASK_NODE) } = useNodeForm(currentNode.value.type)
const configForm = tempConfigForm as Ref<UserTaskFormType> const configForm = tempConfigForm as Ref<UserTaskFormType>
// //
@ -733,13 +757,13 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
configForm.value.approveRatio = node.approveRatio! configForm.value.approveRatio = node.approveRatio!
} }
// 2.3 // 2.3
configForm.value.rejectHandlerType = node.rejectHandler!.type configForm.value.rejectHandlerType = node.rejectHandler?.type
configForm.value.returnNodeId = node.rejectHandler?.returnNodeId configForm.value.returnNodeId = node.rejectHandler?.returnNodeId
const matchNodeList = [] const matchNodeList = []
emits('find:returnTaskNodes', matchNodeList) emits('find:returnTaskNodes', matchNodeList)
returnTaskList.value = matchNodeList returnTaskList.value = matchNodeList
// 2.4 // 2.4
configForm.value.timeoutHandlerEnable = node.timeoutHandler!.enable configForm.value.timeoutHandlerEnable = node.timeoutHandler?.enable
if (node.timeoutHandler?.enable && node.timeoutHandler?.timeDuration) { if (node.timeoutHandler?.enable && node.timeoutHandler?.timeDuration) {
const strTimeDuration = node.timeoutHandler.timeDuration const strTimeDuration = node.timeoutHandler.timeDuration
let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1) let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1)
@ -755,7 +779,11 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
// 2.6 // 2.6
configForm.value.assignStartUserHandlerType = node.assignStartUserHandlerType configForm.value.assignStartUserHandlerType = node.assignStartUserHandlerType
// 3. // 3.
buttonsSetting.value = cloneDeep(node.buttonsSetting) || DEFAULT_BUTTON_SETTING buttonsSetting.value =
cloneDeep(node.buttonsSetting) ||
(node.type === NodeType.TRANSACTOR_NODE
? TRANSACTOR_DEFAULT_BUTTON_SETTING
: DEFAULT_BUTTON_SETTING)
// 4. // 4.
getNodeConfigFormFields(node.fieldsPermission) getNodeConfigFormFields(node.fieldsPermission)
// 5. // 5.
@ -773,7 +801,7 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
header: node.taskAssignListener?.header ?? [], header: node.taskAssignListener?.header ?? [],
body: node.taskAssignListener?.body ?? [] body: node.taskAssignListener?.body ?? []
} }
// 5.3 // 5.3
configForm.value.taskCompleteListenerEnable = node.taskCompleteListener?.enable configForm.value.taskCompleteListenerEnable = node.taskCompleteListener?.enable
configForm.value.taskCompleteListenerPath = node.taskCompleteListener?.path configForm.value.taskCompleteListenerPath = node.taskCompleteListener?.path
configForm.value.taskCompleteListener = { configForm.value.taskCompleteListener = {

View File

@ -28,7 +28,7 @@
{{ currentNode.showText }} {{ currentNode.showText }}
</div> </div>
<div class="node-text" v-else> <div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.USER_TASK_NODE) }} {{ NODE_DEFAULT_TEXT.get(currentNode.type) }}
</div> </div>
<Icon icon="ep:arrow-right-bold" v-if="!readonly" /> <Icon icon="ep:arrow-right-bold" v-if="!readonly" />
</div> </div>