From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue | 1068 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,068 insertions(+), 0 deletions(-)
diff --git a/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue b/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue
new file mode 100644
index 0000000..53058c4
--- /dev/null
+++ b/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue
@@ -0,0 +1,1068 @@
+<template>
+ <el-drawer
+ :append-to-body="true"
+ v-model="settingVisible"
+ :show-close="false"
+ :size="580"
+ :before-close="saveConfig"
+ class="justify-start"
+ >
+ <template #header>
+ <div class="config-header">
+ <input
+ v-if="showInput"
+ type="text"
+ class="config-editable-input"
+ @blur="blurEvent()"
+ v-mountedFocus
+ v-model="nodeName"
+ :placeholder="nodeName"
+ />
+ <div v-else class="node-name">
+ {{ nodeName }}
+ <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
+ </div>
+ <div class="divide-line"></div>
+ </div>
+ </template>
+ <div v-if="currentNode.type === NodeType.USER_TASK_NODE" class="flex flex-items-center mb-3">
+ <span class="font-size-16px mr-3">瀹℃壒绫诲瀷 :</span>
+ <el-radio-group v-model="approveType">
+ <el-radio
+ v-for="(item, index) in APPROVE_TYPE"
+ :key="index"
+ :value="item.value"
+ :label="item.value"
+ >
+ {{ item.label }}
+ </el-radio>
+ </el-radio-group>
+ </div>
+ <el-tabs type="border-card" v-model="activeTabName" v-if="approveType === ApproveType.USER">
+ <el-tab-pane :label="`${nodeTypeName}浜篳" name="user">
+ <div>
+ <el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
+ <el-form-item :label="`${nodeTypeName}浜鸿缃甡" prop="candidateStrategy">
+ <el-radio-group
+ v-model="configForm.candidateStrategy"
+ @change="changeCandidateStrategy"
+ >
+ <el-row>
+ <el-col v-for="(dict, index) in CANDIDATE_STRATEGY" :key="index" :span="8">
+ <el-radio :value="dict.value" :label="dict.value">
+ {{ dict.label }}
+ </el-radio>
+ </el-col>
+ </el-row>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item
+ v-if="configForm.candidateStrategy == CandidateStrategy.ROLE"
+ label="鎸囧畾瑙掕壊"
+ prop="roleIds"
+ >
+ <el-select
+ filterable
+ v-model="configForm.roleIds"
+ clearable
+ multiple
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in roleOptions"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item
+ v-if="
+ configForm.candidateStrategy == CandidateStrategy.DEPT_MEMBER ||
+ configForm.candidateStrategy == CandidateStrategy.DEPT_LEADER ||
+ configForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER
+ "
+ label="鎸囧畾閮ㄩ棬"
+ prop="deptIds"
+ span="24"
+ >
+ <el-tree-select
+ ref="treeRef"
+ v-model="configForm.deptIds"
+ :data="deptTreeOptions"
+ :props="defaultProps"
+ empty-text="鍔犺浇涓紝璇风◢鍚�"
+ multiple
+ node-key="id"
+ :check-strictly="true"
+ style="width: 100%"
+ show-checkbox
+ />
+ </el-form-item>
+ <el-form-item
+ v-if="configForm.candidateStrategy == CandidateStrategy.POST"
+ label="鎸囧畾宀椾綅"
+ prop="postIds"
+ span="24"
+ >
+ <el-select
+ filterable
+ v-model="configForm.postIds"
+ clearable
+ multiple
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in postOptions"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id!"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item
+ v-if="configForm.candidateStrategy == CandidateStrategy.USER"
+ label="鎸囧畾鐢ㄦ埛"
+ prop="userIds"
+ span="24"
+ >
+ <el-select
+ filterable
+ v-model="configForm.userIds"
+ clearable
+ multiple
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in userOptions"
+ :key="item.id"
+ :label="item.nickname"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item
+ v-if="configForm.candidateStrategy === CandidateStrategy.USER_GROUP"
+ label="鎸囧畾鐢ㄦ埛缁�"
+ prop="userGroups"
+ >
+ <el-select
+ filterable
+ v-model="configForm.userGroups"
+ clearable
+ multiple
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in userGroupOptions"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item
+ v-if="configForm.candidateStrategy === CandidateStrategy.FORM_USER"
+ label="琛ㄥ崟鍐呯敤鎴峰瓧娈�"
+ prop="formUser"
+ >
+ <el-select filterable v-model="configForm.formUser" clearable style="width: 100%">
+ <el-option
+ v-for="(item, idx) in userFieldOnFormOptions"
+ :key="idx"
+ :label="item.title"
+ :value="item.field"
+ :disabled="!item.required"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item
+ v-if="configForm.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER"
+ label="琛ㄥ崟鍐呴儴闂ㄥ瓧娈�"
+ prop="formDept"
+ >
+ <el-select filterable v-model="configForm.formDept" clearable style="width: 100%">
+ <el-option
+ v-for="(item, idx) in deptFieldOnFormOptions"
+ :key="idx"
+ :label="item.title"
+ :value="item.field"
+ :disabled="!item.required"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item
+ v-if="
+ configForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
+ configForm.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
+ configForm.candidateStrategy ==
+ CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER ||
+ configForm.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
+ "
+ :label="deptLevelLabel!"
+ prop="deptLevel"
+ span="24"
+ >
+ <el-select filterable v-model="configForm.deptLevel" clearable>
+ <el-option
+ v-for="(item, index) in MULTI_LEVEL_DEPT"
+ :key="index"
+ :label="item.label"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ <!-- TODO @jason锛氬悗缁鏀寔閫夋嫨宸茬粡瀛樺ソ鐨勮〃杈惧紡 -->
+ <el-form-item
+ v-if="configForm.candidateStrategy === CandidateStrategy.EXPRESSION"
+ label="娴佺▼琛ㄨ揪寮�"
+ prop="expression"
+ >
+ <el-input
+ type="textarea"
+ v-model="configForm.expression"
+ clearable
+ style="width: 100%"
+ />
+ </el-form-item>
+ <el-form-item :label="`澶氫汉${nodeTypeName}鏂瑰紡`" prop="approveMethod">
+ <el-radio-group v-model="configForm.approveMethod" @change="approveMethodChanged">
+ <div class="flex-col">
+ <div
+ v-for="(item, index) in APPROVE_METHODS"
+ :key="index"
+ class="flex items-center"
+ >
+ <el-radio :value="item.value" :label="item.value">
+ {{ item.label }}
+ </el-radio>
+ <el-form-item prop="approveRatio">
+ <el-input-number
+ v-model="configForm.approveRatio"
+ :min="10"
+ :max="100"
+ :step="10"
+ size="small"
+ v-if="
+ item.value === ApproveMethodType.APPROVE_BY_RATIO &&
+ configForm.approveMethod === ApproveMethodType.APPROVE_BY_RATIO
+ "
+ />
+ </el-form-item>
+ </div>
+ </div>
+ </el-radio-group>
+ </el-form-item>
+
+ <div v-if="currentNode.type === NodeType.USER_TASK_NODE">
+ <el-divider content-position="left">瀹℃壒浜烘嫆缁濇椂</el-divider>
+ <el-form-item prop="rejectHandlerType">
+ <el-radio-group v-model="configForm.rejectHandlerType">
+ <div class="flex-col">
+ <div v-for="(item, index) in REJECT_HANDLER_TYPES" :key="index">
+ <el-radio :key="item.value" :value="item.value" :label="item.label" />
+ </div>
+ </div>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item
+ v-if="configForm.rejectHandlerType == RejectHandlerType.RETURN_USER_TASK"
+ label="椹冲洖鑺傜偣"
+ 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>
+ </div>
+
+ <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
+ label="鎵ц鍔ㄤ綔"
+ prop="timeoutHandlerType"
+ v-if="configForm.timeoutHandlerEnable"
+ >
+ <el-radio-group
+ v-model="configForm.timeoutHandlerType"
+ @change="timeoutHandlerTypeChanged"
+ >
+ <el-radio-button
+ v-for="item in TIMEOUT_HANDLER_TYPES"
+ :key="item.value"
+ :value="item.value"
+ :label="item.label"
+ />
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item label="瓒呮椂鏃堕棿璁剧疆" v-if="configForm.timeoutHandlerEnable">
+ <span class="mr-2">褰撹秴杩�</span>
+ <el-form-item prop="timeDuration">
+ <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">{{ nodeTypeName }}浜轰负绌烘椂</el-divider>
+ <el-form-item prop="assignEmptyHandlerType">
+ <el-radio-group v-model="configForm.assignEmptyHandlerType">
+ <div class="flex-col">
+ <div v-for="(item, index) in ASSIGN_EMPTY_HANDLER_TYPES" :key="index">
+ <el-radio :key="item.value" :value="item.value" :label="item.label" />
+ </div>
+ </div>
+ </el-radio-group>
+ </el-form-item>
+ <el-form-item
+ v-if="configForm.assignEmptyHandlerType == AssignEmptyHandlerType.ASSIGN_USER"
+ label="鎸囧畾鐢ㄦ埛"
+ prop="assignEmptyHandlerUserIds"
+ span="24"
+ >
+ <el-select
+ filterable
+ v-model="configForm.assignEmptyHandlerUserIds"
+ clearable
+ multiple
+ style="width: 100%"
+ >
+ <el-option
+ v-for="item in userOptions"
+ :key="item.id"
+ :label="item.nickname"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+
+ <div v-if="currentNode.type === NodeType.USER_TASK_NODE">
+ <el-divider content-position="left">瀹℃壒浜轰笌鎻愪氦浜轰负鍚屼竴浜烘椂</el-divider>
+ <el-form-item prop="assignStartUserHandlerType">
+ <el-radio-group v-model="configForm.assignStartUserHandlerType">
+ <div class="flex-col">
+ <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>
+ </el-radio-group>
+ </el-form-item>
+ </div>
+
+ <div v-if="currentNode.type === NodeType.USER_TASK_NODE">
+ <el-divider content-position="left">鏄惁闇�瑕佺鍚�</el-divider>
+ <el-form-item prop="signEnable">
+ <el-switch v-model="configForm.signEnable" active-text="鏄�" inactive-text="鍚�" />
+ </el-form-item>
+ </div>
+
+ <div v-if="currentNode.type === NodeType.USER_TASK_NODE">
+ <el-divider content-position="left">瀹℃壒鎰忚</el-divider>
+ <el-form-item prop="reasonRequire">
+ <el-switch
+ v-model="configForm.reasonRequire"
+ active-text="蹇呭~"
+ inactive-text="闈炲繀濉�"
+ />
+ </el-form-item>
+ </div>
+ <div>
+ <el-divider content-position="left">璺宠繃琛ㄨ揪寮�</el-divider>
+ <el-form-item prop="skipExpression">
+ <el-input v-model="configForm.skipExpression" type="textarea" />
+ </el-form-item>
+ </div>
+ </el-form>
+ </div>
+ </el-tab-pane>
+ <el-tab-pane
+ label="鎿嶄綔鎸夐挳璁剧疆"
+ v-if="currentNode.type === NodeType.USER_TASK_NODE"
+ name="buttons"
+ >
+ <div class="button-setting-pane">
+ <div class="button-setting-desc">鎿嶄綔鎸夐挳</div>
+ <div class="button-setting-title">
+ <div class="button-title-label">鎿嶄綔鎸夐挳</div>
+ <div class="pl-4 button-title-label">鏄剧ず鍚嶇О</div>
+ <div class="button-title-label">鍚敤</div>
+ </div>
+ <div class="button-setting-item" v-for="(item, index) in buttonsSetting" :key="index">
+ <div class="button-setting-item-label"> {{ OPERATION_BUTTON_NAME.get(item.id) }} </div>
+ <div class="button-setting-item-label">
+ <input
+ type="text"
+ class="editable-title-input"
+ @blur="btnDisplayNameBlurEvent(index)"
+ v-mountedFocus
+ v-model="item.displayName"
+ :placeholder="item.displayName"
+ v-if="btnDisplayNameEdit[index]"
+ />
+ <el-button v-else text @click="changeBtnDisplayName(index)"
+ >{{ item.displayName }} <Icon icon="ep:edit"
+ /></el-button>
+ </div>
+ <div class="button-setting-item-label">
+ <el-switch v-model="item.enable" />
+ </div>
+ </div>
+ </div>
+ </el-tab-pane>
+ <el-tab-pane label="琛ㄥ崟瀛楁鏉冮檺" name="fields" v-if="formType === 10">
+ <div class="field-setting-pane">
+ <div class="field-setting-desc">瀛楁鏉冮檺</div>
+ <div class="field-permit-title">
+ <div class="setting-title-label first-title"> 瀛楁鍚嶇О </div>
+ <div class="other-titles">
+ <span class="setting-title-label cursor-pointer" @click="updatePermission('READ')">
+ 鍙
+ </span>
+ <span class="setting-title-label cursor-pointer" @click="updatePermission('WRITE')">
+ 鍙紪杈�
+ </span>
+ <span class="setting-title-label cursor-pointer" @click="updatePermission('NONE')">
+ 闅愯棌
+ </span>
+ </div>
+ </div>
+ <div
+ class="field-setting-item"
+ v-for="(item, index) in fieldsPermissionConfig"
+ :key="index"
+ >
+ <div class="field-setting-item-label"> {{ item.title }} </div>
+ <el-radio-group class="field-setting-item-group" v-model="item.permission">
+ <div class="item-radio-wrap">
+ <el-radio
+ :value="FieldPermissionType.READ"
+ size="large"
+ :label="FieldPermissionType.READ"
+ ><span></span
+ ></el-radio>
+ </div>
+ <div class="item-radio-wrap">
+ <el-radio
+ :value="FieldPermissionType.WRITE"
+ size="large"
+ :label="FieldPermissionType.WRITE"
+ ><span></span
+ ></el-radio>
+ </div>
+ <div class="item-radio-wrap">
+ <el-radio
+ :value="FieldPermissionType.NONE"
+ size="large"
+ :label="FieldPermissionType.NONE"
+ ><span></span
+ ></el-radio>
+ </div>
+ </el-radio-group>
+ </div>
+ </div>
+ </el-tab-pane>
+ <el-tab-pane label="鐩戝惉鍣�" name="listener">
+ <UserTaskListener
+ ref="userTaskListenerRef"
+ v-model="configForm"
+ :form-field-options="formFieldOptions"
+ />
+ </el-tab-pane>
+ </el-tabs>
+ <template #footer>
+ <el-divider />
+ <div>
+ <el-button type="primary" @click="saveConfig">纭� 瀹�</el-button>
+ <el-button @click="closeDrawer">鍙� 娑�</el-button>
+ </div>
+ </template>
+ </el-drawer>
+</template>
+
+<script setup lang="ts">
+import {
+ SimpleFlowNode,
+ APPROVE_TYPE,
+ ApproveType,
+ APPROVE_METHODS,
+ CandidateStrategy,
+ NodeType,
+ ApproveMethodType,
+ TimeUnitType,
+ RejectHandlerType,
+ TIMEOUT_HANDLER_TYPES,
+ TIME_UNIT_TYPES,
+ REJECT_HANDLER_TYPES,
+ DEFAULT_BUTTON_SETTING,
+ OPERATION_BUTTON_NAME,
+ ButtonSetting,
+ MULTI_LEVEL_DEPT,
+ CANDIDATE_STRATEGY,
+ ASSIGN_START_USER_HANDLER_TYPES,
+ TimeoutHandlerType,
+ ASSIGN_EMPTY_HANDLER_TYPES,
+ AssignEmptyHandlerType,
+ FieldPermissionType,
+ ProcessVariableEnum,
+ TRANSACTOR_DEFAULT_BUTTON_SETTING
+} from '../consts'
+
+import {
+ useWatchNode,
+ useNodeName,
+ useFormFieldsPermission,
+ useNodeForm,
+ UserTaskFormType,
+ useDrawer
+} from '../node'
+import { defaultProps } from '@/utils/tree'
+import { cloneDeep } from 'lodash-es'
+import { convertTimeUnit, getApproveTypeText } from '../utils'
+import UserTaskListener from './components/UserTaskListener.vue'
+defineOptions({
+ name: 'UserTaskNodeConfig'
+})
+const props = defineProps({
+ flowNode: {
+ type: Object as () => SimpleFlowNode,
+ required: true
+ }
+})
+const emits = defineEmits<{
+ 'find:returnTaskNodes': [nodeList: SimpleFlowNode[]]
+}>()
+const deptLevelLabel = computed(() => {
+ let label = '閮ㄩ棬璐熻矗浜烘潵婧�'
+ if (configForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) {
+ label = label + '(鎸囧畾閮ㄩ棬鍚戜笂)'
+ } else if (configForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER) {
+ label = label + '(琛ㄥ崟鍐呴儴闂ㄥ悜涓�)'
+ } else {
+ label = label + '(鍙戣捣浜洪儴闂ㄥ悜涓�)'
+ }
+ return label
+})
+// 鐩戞帶鑺傜偣鐨勫彉鍖�
+const currentNode = useWatchNode(props)
+// 鎶藉眽閰嶇疆
+const { settingVisible, closeDrawer, openDrawer } = useDrawer()
+// 鑺傜偣鍚嶇О閰嶇疆
+const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.USER_TASK_NODE)
+// 婵�娲荤殑 Tab 鏍囩椤�
+const activeTabName = ref('user')
+// 琛ㄥ崟瀛楁鏉冮檺璁剧疆
+const { formType, fieldsPermissionConfig, formFieldOptions, getNodeConfigFormFields } =
+ useFormFieldsPermission(FieldPermissionType.READ)
+// 琛ㄥ崟鍐呯敤鎴峰瓧娈甸�夐」, 蹇呴』鏄繀濉拰鐢ㄦ埛閫夋嫨鍣�
+const userFieldOnFormOptions = computed(() => {
+ // 鍥哄畾娣诲姞鍙戣捣浜� ID 瀛楁
+ formFieldOptions.unshift({
+ field: ProcessVariableEnum.START_USER_ID,
+ title: '鍙戣捣浜�',
+ type: 'UserSelect',
+ required: true
+ })
+ return formFieldOptions.filter((item) => item.type === 'UserSelect')
+})
+// 琛ㄥ崟鍐呴儴闂ㄥ瓧娈甸�夐」, 蹇呴』鏄繀濉拰閮ㄩ棬閫夋嫨鍣�
+const deptFieldOnFormOptions = computed(() => {
+ return formFieldOptions.filter((item) => item.type === 'DeptSelect')
+})
+// 鎿嶄綔鎸夐挳璁剧疆
+const { buttonsSetting, btnDisplayNameEdit, changeBtnDisplayName, btnDisplayNameBlurEvent } =
+ useButtonsSetting()
+const approveType = ref(ApproveType.USER)
+// 瀹℃壒浜鸿〃鍗曡缃�
+const formRef = ref() // 琛ㄥ崟 Ref
+// 琛ㄥ崟鏍¢獙瑙勫垯
+const formRules = reactive({
+ candidateStrategy: [{ required: true, message: '瀹℃壒浜鸿缃笉鑳戒负绌�', trigger: 'change' }],
+ userIds: [{ required: true, message: '鐢ㄦ埛涓嶈兘涓虹┖', trigger: 'change' }],
+ roleIds: [{ required: true, message: '瑙掕壊涓嶈兘涓虹┖', trigger: 'change' }],
+ deptIds: [{ required: true, message: '閮ㄩ棬涓嶈兘涓虹┖', trigger: 'change' }],
+ userGroups: [{ required: true, message: '鐢ㄦ埛缁勪笉鑳戒负绌�', trigger: 'change' }],
+ formUser: [{ required: true, message: '琛ㄥ崟鍐呯敤鎴峰瓧娈典笉鑳戒负绌�', trigger: 'change' }],
+ formDept: [{ required: true, message: '琛ㄥ崟鍐呴儴闂ㄥ瓧娈典笉鑳戒负绌�', trigger: 'change' }],
+ postIds: [{ required: true, message: '宀椾綅涓嶈兘涓虹┖', trigger: 'change' }],
+ expression: [{ required: true, message: '娴佺▼琛ㄨ揪寮忎笉鑳戒负绌�', trigger: 'blur' }],
+ approveMethod: [{ required: true, message: '澶氫汉瀹℃壒鏂瑰紡涓嶈兘涓虹┖', trigger: 'change' }],
+ approveRatio: [{ required: true, message: '閫氳繃姣斾緥涓嶈兘涓虹┖', trigger: 'blur' }],
+ returnNodeId: [{ required: true, message: '椹冲洖鑺傜偣涓嶈兘涓虹┖', trigger: 'change' }],
+ timeoutHandlerEnable: [{ required: true }],
+ timeoutHandlerType: [{ required: true }],
+ timeDuration: [{ required: true, message: '瓒呮椂鏃堕棿涓嶈兘涓虹┖', trigger: 'blur' }],
+ maxRemindCount: [{ required: true, message: '鎻愰啋娆℃暟涓嶈兘涓虹┖', trigger: 'blur' }],
+ assignEmptyHandlerType: [{ required: true }],
+ assignEmptyHandlerUserIds: [{ required: true, message: '鐢ㄦ埛涓嶈兘涓虹┖', trigger: 'change' }],
+ assignStartUserHandlerType: [{ required: true }]
+})
+
+const {
+ configForm: tempConfigForm,
+ roleOptions,
+ postOptions,
+ userOptions,
+ userGroupOptions,
+ deptTreeOptions,
+ handleCandidateParam,
+ parseCandidateParam,
+ getShowText
+} = useNodeForm(currentNode.value.type)
+const configForm = tempConfigForm as Ref<UserTaskFormType>
+
+// 鏀瑰彉瀹℃壒浜鸿缃瓥鐣�
+const changeCandidateStrategy = () => {
+ configForm.value.userIds = []
+ configForm.value.deptIds = []
+ configForm.value.roleIds = []
+ configForm.value.postIds = []
+ configForm.value.userGroups = []
+ configForm.value.deptLevel = 1
+ configForm.value.formUser = ''
+ configForm.value.formDept = ''
+ configForm.value.approveMethod = ApproveMethodType.SEQUENTIAL_APPROVE
+}
+
+// 瀹℃壒鏂瑰紡鏀瑰彉
+const approveMethodChanged = () => {
+ configForm.value.rejectHandlerType = RejectHandlerType.FINISH_PROCESS
+ if (configForm.value.approveMethod === ApproveMethodType.APPROVE_BY_RATIO) {
+ configForm.value.approveRatio = 100
+ }
+ formRef.value.clearValidate('approveRatio')
+}
+// 瀹℃壒鎷掔粷 鍙��鍥炵殑鑺傜偣
+const returnTaskList = ref<SimpleFlowNode[]>([])
+// 瀹℃壒浜鸿秴鏃舵湭澶勭悊璁剧疆
+const {
+ timeoutHandlerChange,
+ cTimeoutType,
+ timeoutHandlerTypeChanged,
+ timeUnit,
+ timeUnitChange,
+ isoTimeDuration,
+ cTimeoutMaxRemindCount
+} = useTimeoutHandler()
+
+const userTaskListenerRef = ref()
+
+/** 鑺傜偣绫诲瀷鍚嶇О */
+const nodeTypeName = computed(() => {
+ return currentNode.value.type === NodeType.TRANSACTOR_NODE ? '鍔炵悊' : '瀹℃壒'
+})
+
+/** 淇濆瓨閰嶇疆 */
+const saveConfig = async () => {
+ // activeTabName.value = 'user'
+ // 璁剧疆瀹℃壒鑺傜偣鍚嶇О
+ currentNode.value.name = nodeName.value!
+ // 璁剧疆瀹℃壒绫诲瀷
+ currentNode.value.approveType = approveType.value
+ // 濡傛灉涓嶆槸浜哄伐瀹℃壒銆傝繑鍥�
+ if (approveType.value !== ApproveType.USER) {
+ currentNode.value.showText = getApproveTypeText(approveType.value)
+ settingVisible.value = false
+ return true
+ }
+
+ if (!formRef) return false
+ if (!userTaskListenerRef) return false
+ const valid = (await formRef.value.validate()) && (await userTaskListenerRef.value.validate())
+ if (!valid) return false
+ const showText = getShowText()
+ if (!showText) return false
+
+ currentNode.value.candidateStrategy = configForm.value.candidateStrategy
+ // 澶勭悊 candidateParam 鍙傛暟
+ currentNode.value.candidateParam = handleCandidateParam()
+ // 璁剧疆瀹℃壒鏂瑰紡
+ currentNode.value.approveMethod = configForm.value.approveMethod
+ if (configForm.value.approveMethod === ApproveMethodType.APPROVE_BY_RATIO) {
+ currentNode.value.approveRatio = configForm.value.approveRatio
+ }
+ // 璁剧疆鎷掔粷澶勭悊
+ currentNode.value.rejectHandler = {
+ type: configForm.value.rejectHandlerType!,
+ returnNodeId: configForm.value.returnNodeId
+ }
+ // 璁剧疆瓒呮椂澶勭悊
+ currentNode.value.timeoutHandler = {
+ enable: configForm.value.timeoutHandlerEnable!,
+ type: cTimeoutType.value,
+ timeDuration: isoTimeDuration.value,
+ maxRemindCount: cTimeoutMaxRemindCount.value
+ }
+ // 璁剧疆瀹℃壒浜轰负绌烘椂
+ currentNode.value.assignEmptyHandler = {
+ type: configForm.value.assignEmptyHandlerType!,
+ userIds:
+ configForm.value.assignEmptyHandlerType === AssignEmptyHandlerType.ASSIGN_USER
+ ? configForm.value.assignEmptyHandlerUserIds
+ : undefined
+ }
+ // 璁剧疆瀹℃壒浜轰笌鍙戣捣浜虹浉鍚屾椂
+ currentNode.value.assignStartUserHandlerType = configForm.value.assignStartUserHandlerType
+ // 璁剧疆琛ㄥ崟鏉冮檺
+ currentNode.value.fieldsPermission = fieldsPermissionConfig.value
+ // 璁剧疆鎸夐挳鏉冮檺
+ currentNode.value.buttonsSetting = buttonsSetting.value
+ // 鍒涘缓浠诲姟鐩戝惉鍣�
+ currentNode.value.taskCreateListener = {
+ enable: configForm.value.taskCreateListenerEnable ?? false,
+ path: configForm.value.taskCreateListenerPath,
+ header: configForm.value.taskCreateListener?.header,
+ body: configForm.value.taskCreateListener?.body
+ }
+ // 鎸囨淳浠诲姟鐩戝惉鍣�
+ currentNode.value.taskAssignListener = {
+ enable: configForm.value.taskAssignListenerEnable ?? false,
+ path: configForm.value.taskAssignListenerPath,
+ header: configForm.value.taskAssignListener?.header,
+ body: configForm.value.taskAssignListener?.body
+ }
+ // 瀹屾垚浠诲姟鐩戝惉鍣�
+ currentNode.value.taskCompleteListener = {
+ enable: configForm.value.taskCompleteListenerEnable ?? false,
+ path: configForm.value.taskCompleteListenerPath,
+ header: configForm.value.taskCompleteListener?.header,
+ body: configForm.value.taskCompleteListener?.body
+ }
+ // 绛惧悕
+ currentNode.value.signEnable = configForm.value.signEnable
+ // 瀹℃壒鎰忚
+ currentNode.value.reasonRequire = configForm.value.reasonRequire
+ // 璺宠繃琛ㄨ揪寮�
+ currentNode.value.skipExpression = configForm.value.skipExpression
+
+ currentNode.value.showText = showText
+ settingVisible.value = false
+ return true
+}
+
+/** 鏄剧ず瀹℃壒鑺傜偣閰嶇疆锛� 鐢辩埗缁勪欢浼犺繃鏉� */
+const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
+ nodeName.value = node.name
+ // 1 瀹℃壒绫诲瀷
+ approveType.value = node.approveType ? node.approveType : ApproveType.USER
+ // 濡傛灉瀹℃壒绫诲瀷涓嶆槸浜哄伐瀹℃壒杩斿洖
+ if (approveType.value !== ApproveType.USER) {
+ return
+ }
+
+ //2.1 瀹℃壒浜鸿缃�
+ configForm.value.candidateStrategy = node.candidateStrategy!
+ // 瑙f瀽鍊欓�変汉鍙傛暟
+ parseCandidateParam(node.candidateStrategy!, node?.candidateParam)
+ // 2.2 璁剧疆瀹℃壒鏂瑰紡
+ configForm.value.approveMethod = node.approveMethod!
+ if (node.approveMethod == ApproveMethodType.APPROVE_BY_RATIO) {
+ configForm.value.approveRatio = node.approveRatio!
+ }
+ // 2.3 璁剧疆瀹℃壒鎷掔粷澶勭悊
+ configForm.value.rejectHandlerType = node.rejectHandler?.type
+ configForm.value.returnNodeId = node.rejectHandler?.returnNodeId
+ const matchNodeList = []
+ emits('find:returnTaskNodes', matchNodeList)
+ returnTaskList.value = matchNodeList
+ // 2.4 璁剧疆瀹℃壒瓒呮椂澶勭悊
+ configForm.value.timeoutHandlerEnable = node.timeoutHandler?.enable
+ if (node.timeoutHandler?.enable && node.timeoutHandler?.timeDuration) {
+ const strTimeDuration = node.timeoutHandler.timeDuration
+ let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1)
+ let parseTimeUnit = strTimeDuration.slice(strTimeDuration.length - 1)
+ configForm.value.timeDuration = parseInt(parseTime)
+ timeUnit.value = convertTimeUnit(parseTimeUnit)
+ }
+ configForm.value.timeoutHandlerType = node.timeoutHandler?.type
+ configForm.value.maxRemindCount = node.timeoutHandler?.maxRemindCount
+ // 2.5 璁剧疆瀹℃壒浜轰负绌烘椂
+ configForm.value.assignEmptyHandlerType = node.assignEmptyHandler?.type
+ configForm.value.assignEmptyHandlerUserIds = node.assignEmptyHandler?.userIds
+ // 2.6 璁剧疆鐢ㄦ埛浠诲姟鐨勫鎵逛汉涓庡彂璧蜂汉鐩稿悓鏃�
+ configForm.value.assignStartUserHandlerType = node.assignStartUserHandlerType
+ // 3. 鎿嶄綔鎸夐挳璁剧疆
+ buttonsSetting.value =
+ cloneDeep(node.buttonsSetting) ||
+ (node.type === NodeType.TRANSACTOR_NODE
+ ? TRANSACTOR_DEFAULT_BUTTON_SETTING
+ : DEFAULT_BUTTON_SETTING)
+ // 4. 琛ㄥ崟瀛楁鏉冮檺閰嶇疆
+ getNodeConfigFormFields(node.fieldsPermission)
+ // 5. 鐩戝惉鍣�
+ // 5.1 鍒涘缓浠诲姟
+ configForm.value.taskCreateListenerEnable = node.taskCreateListener?.enable
+ configForm.value.taskCreateListenerPath = node.taskCreateListener?.path
+ configForm.value.taskCreateListener = {
+ header: node.taskCreateListener?.header ?? [],
+ body: node.taskCreateListener?.body ?? []
+ }
+ // 5.2 鎸囨淳浠诲姟
+ configForm.value.taskAssignListenerEnable = node.taskAssignListener?.enable
+ configForm.value.taskAssignListenerPath = node.taskAssignListener?.path
+ configForm.value.taskAssignListener = {
+ header: node.taskAssignListener?.header ?? [],
+ body: node.taskAssignListener?.body ?? []
+ }
+ // 5.3 瀹屾垚浠诲姟
+ configForm.value.taskCompleteListenerEnable = node.taskCompleteListener?.enable
+ configForm.value.taskCompleteListenerPath = node.taskCompleteListener?.path
+ configForm.value.taskCompleteListener = {
+ header: node.taskCompleteListener?.header ?? [],
+ body: node.taskCompleteListener?.body ?? []
+ }
+ // 6. 绛惧悕
+ configForm.value.signEnable = node?.signEnable ?? false
+ // 7. 瀹℃壒鎰忚
+ configForm.value.reasonRequire = node?.reasonRequire ?? false
+ // 8. 璺宠繃琛ㄨ揪寮�
+ configForm.value.skipExpression = node?.skipExpression ?? ''
+}
+
+defineExpose({ openDrawer, showUserTaskNodeConfig }) // 鏆撮湶鏂规硶缁欑埗缁勪欢
+
+/** 鎿嶄綔鎸夐挳璁剧疆 */
+function useButtonsSetting() {
+ const buttonsSetting = ref<ButtonSetting[]>()
+ // 鎿嶄綔鎸夐挳鏄剧ず鍚嶇О鍙紪杈�
+ const btnDisplayNameEdit = ref<boolean[]>([])
+ const changeBtnDisplayName = (index: number) => {
+ btnDisplayNameEdit.value[index] = true
+ }
+ const btnDisplayNameBlurEvent = (index: number) => {
+ btnDisplayNameEdit.value[index] = false
+ const buttonItem = buttonsSetting.value![index]
+ buttonItem.displayName = buttonItem.displayName || OPERATION_BUTTON_NAME.get(buttonItem.id)!
+ }
+ return {
+ buttonsSetting,
+ btnDisplayNameEdit,
+ changeBtnDisplayName,
+ btnDisplayNameBlurEvent
+ }
+}
+
+/** 瀹℃壒浜鸿秴鏃舵湭澶勭悊閰嶇疆 */
+function useTimeoutHandler() {
+ // 鏃堕棿鍗曚綅
+ const timeUnit = ref(TimeUnitType.HOUR)
+
+ // 瓒呮椂寮�鍏虫敼鍙�
+ const timeoutHandlerChange = () => {
+ if (configForm.value.timeoutHandlerEnable) {
+ timeUnit.value = 2
+ configForm.value.timeDuration = 6
+ configForm.value.timeoutHandlerType = 1
+ configForm.value.maxRemindCount = 1
+ }
+ }
+ // 瓒呮椂鎵ц鐨勫姩浣�
+ const cTimeoutType = computed(() => {
+ if (!configForm.value.timeoutHandlerEnable) {
+ return undefined
+ }
+ return configForm.value.timeoutHandlerType
+ })
+
+ // 瓒呮椂澶勭悊鍔ㄤ綔鏀瑰彉
+ const timeoutHandlerTypeChanged = () => {
+ if (configForm.value.timeoutHandlerType === TimeoutHandlerType.REMINDER) {
+ configForm.value.maxRemindCount = 1 // 瓒呮椂鎻愰啋娆℃暟锛岄粯璁や负1
+ }
+ }
+
+ // 鏃堕棿鍗曚綅鏀瑰彉
+ const timeUnitChange = () => {
+ // 鍒嗛挓锛岄粯璁ゆ槸 60 鍒嗛挓
+ if (timeUnit.value === TimeUnitType.MINUTE) {
+ configForm.value.timeDuration = 60
+ }
+ // 灏忔椂锛岄粯璁ゆ槸 6 涓皬鏃�
+ if (timeUnit.value === TimeUnitType.HOUR) {
+ configForm.value.timeDuration = 6
+ }
+ // 澶╋紝 榛樿 1澶�
+ if (timeUnit.value === TimeUnitType.DAY) {
+ configForm.value.timeDuration = 1
+ }
+ }
+ // 瓒呮椂鏃堕棿鐨� ISO 琛ㄧず
+ const isoTimeDuration = computed(() => {
+ if (!configForm.value.timeoutHandlerEnable) {
+ return undefined
+ }
+ let strTimeDuration = 'PT'
+ if (timeUnit.value === TimeUnitType.MINUTE) {
+ strTimeDuration += configForm.value.timeDuration + 'M'
+ }
+ if (timeUnit.value === TimeUnitType.HOUR) {
+ strTimeDuration += configForm.value.timeDuration + 'H'
+ }
+ if (timeUnit.value === TimeUnitType.DAY) {
+ strTimeDuration += configForm.value.timeDuration + 'D'
+ }
+ return strTimeDuration
+ })
+
+ // 瓒呮椂鏈�澶ф彁閱掓鏁�
+ const cTimeoutMaxRemindCount = computed(() => {
+ if (!configForm.value.timeoutHandlerEnable) {
+ return undefined
+ }
+ if (configForm.value.timeoutHandlerType !== TimeoutHandlerType.REMINDER) {
+ return undefined
+ }
+ return configForm.value.maxRemindCount
+ })
+
+ return {
+ timeoutHandlerChange,
+ cTimeoutType,
+ timeoutHandlerTypeChanged,
+ timeUnit,
+ timeUnitChange,
+ isoTimeDuration,
+ cTimeoutMaxRemindCount
+ }
+}
+
+/** 鎵归噺鏇存柊鏉冮檺 */
+const updatePermission = (type: string) => {
+ fieldsPermissionConfig.value.forEach((field) => {
+ field.permission =
+ type === 'READ'
+ ? FieldPermissionType.READ
+ : type === 'WRITE'
+ ? FieldPermissionType.WRITE
+ : FieldPermissionType.NONE
+ })
+}
+</script>
+
+<style lang="scss" scoped>
+.button-setting-pane {
+ display: flex;
+ flex-direction: column;
+ font-size: 14px;
+
+ .button-setting-desc {
+ padding-right: 8px;
+ margin-bottom: 16px;
+ font-size: 16px;
+ font-weight: 700;
+ }
+
+ .button-setting-title {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 45px;
+ padding-left: 12px;
+ background-color: #f8fafc0a;
+ border: 1px solid #1f38581a;
+
+ & > :first-child {
+ width: 100px !important;
+ text-align: left !important;
+ }
+
+ & > :last-child {
+ text-align: center !important;
+ }
+
+ .button-title-label {
+ width: 150px;
+ font-size: 13px;
+ font-weight: 700;
+ color: #000;
+ text-align: left;
+ }
+ }
+
+ .button-setting-item {
+ align-items: center;
+ display: flex;
+ justify-content: space-between;
+ height: 38px;
+ padding-left: 12px;
+ border: 1px solid #1f38581a;
+ border-top: 0;
+
+ & > :first-child {
+ width: 100px !important;
+ }
+
+ & > :last-child {
+ text-align: center !important;
+ }
+
+ .button-setting-item-label {
+ width: 150px;
+ overflow: hidden;
+ text-align: left;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .editable-title-input {
+ height: 24px;
+ max-width: 130px;
+ margin-left: 4px;
+ line-height: 24px;
+ border: 1px solid #d9d9d9;
+ border-radius: 4px;
+ transition: all 0.3s;
+
+ &:focus {
+ border-color: #40a9ff;
+ outline: 0;
+ box-shadow: 0 0 0 2px rgb(24 144 255 / 20%);
+ }
+ }
+ }
+}
+</style>
--
Gitblit v1.8.0