|
@@ -0,0 +1,901 @@
|
|
|
+<template>
|
|
|
+ <el-drawer
|
|
|
+ :append-to-body="true"
|
|
|
+ v-model="settingVisible"
|
|
|
+ :show-close="false"
|
|
|
+ :size="550"
|
|
|
+ :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 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="审批人" name="user">
|
|
|
+ <div>
|
|
|
+ <el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
|
|
|
+ <el-form-item label="审批人设置" prop="candidateStrategy">
|
|
|
+ <el-radio-group
|
|
|
+ v-model="configForm.candidateStrategy"
|
|
|
+ @change="changeCandidateStrategy"
|
|
|
+ >
|
|
|
+ <el-radio
|
|
|
+ v-for="(dict, index) in CANDIDATE_STRATEGY"
|
|
|
+ :key="index"
|
|
|
+ :value="dict.value"
|
|
|
+ :label="dict.value"
|
|
|
+ >
|
|
|
+ {{ dict.label }}
|
|
|
+ </el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item
|
|
|
+ v-if="configForm.candidateStrategy == CandidateStrategy.ROLE"
|
|
|
+ label="指定角色"
|
|
|
+ prop="roleIds"
|
|
|
+ >
|
|
|
+ <el-select 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.MULTI_LEVEL_DEPT_LEADER ||
|
|
|
+ configForm.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
|
|
|
+ configForm.candidateStrategy == CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER
|
|
|
+ "
|
|
|
+ :label="deptLevelLabel!"
|
|
|
+ prop="deptLevel"
|
|
|
+ span="24"
|
|
|
+ >
|
|
|
+ <el-select 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>
|
|
|
+ <el-form-item
|
|
|
+ v-if="configForm.candidateStrategy == CandidateStrategy.POST"
|
|
|
+ label="指定岗位"
|
|
|
+ prop="postIds"
|
|
|
+ span="24"
|
|
|
+ >
|
|
|
+ <el-select 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
|
|
|
+ v-model="configForm.userIds"
|
|
|
+ clearable
|
|
|
+ multiple
|
|
|
+ style="width: 100%"
|
|
|
+ @change="changedCandidateUsers"
|
|
|
+ >
|
|
|
+ <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 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>
|
|
|
+ <!-- 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="多人审批方式" 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"
|
|
|
+ :disabled="
|
|
|
+ item.value !== ApproveMethodType.RANDOM_SELECT_ONE_APPROVE &&
|
|
|
+ notAllowedMultiApprovers
|
|
|
+ "
|
|
|
+ >
|
|
|
+ {{ 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>
|
|
|
+
|
|
|
+ <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 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
|
|
|
+ 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
|
|
|
+ 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>
|
|
|
+
|
|
|
+ <el-divider content-position="left">审批人为空时</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
|
|
|
+ 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>
|
|
|
+
|
|
|
+ <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>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="操作按钮设置" 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">只读</span>
|
|
|
+ <span class="setting-title-label">可编辑</span>
|
|
|
+ <span class="setting-title-label">隐藏</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-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
|
|
|
+} 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'
|
|
|
+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 {
|
|
|
+ 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, getNodeConfigFormFields } = useFormFieldsPermission(
|
|
|
+ FieldPermissionType.READ
|
|
|
+)
|
|
|
+// 操作按钮设置
|
|
|
+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' }],
|
|
|
+ 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(NodeType.USER_TASK_NODE)
|
|
|
+const configForm = tempConfigForm as Ref<UserTaskFormType>
|
|
|
+// 不允许多人审批
|
|
|
+const notAllowedMultiApprovers = ref(false)
|
|
|
+// 改变审批人设置策略
|
|
|
+const changeCandidateStrategy = () => {
|
|
|
+ configForm.value.userIds = []
|
|
|
+ configForm.value.deptIds = []
|
|
|
+ configForm.value.roleIds = []
|
|
|
+ configForm.value.postIds = []
|
|
|
+ configForm.value.userGroups = []
|
|
|
+ configForm.value.deptLevel = 1
|
|
|
+ configForm.value.approveMethod = ApproveMethodType.SEQUENTIAL_APPROVE
|
|
|
+ if (
|
|
|
+ configForm.value.candidateStrategy === CandidateStrategy.START_USER ||
|
|
|
+ configForm.value.candidateStrategy === CandidateStrategy.USER
|
|
|
+ ) {
|
|
|
+ notAllowedMultiApprovers.value = true
|
|
|
+ } else {
|
|
|
+ notAllowedMultiApprovers.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+// 改变审批候选人
|
|
|
+const changedCandidateUsers = () => {
|
|
|
+ if (
|
|
|
+ configForm.value.userIds &&
|
|
|
+ configForm.value.userIds?.length <= 1 &&
|
|
|
+ configForm.value.candidateStrategy === CandidateStrategy.USER
|
|
|
+ ) {
|
|
|
+ configForm.value.approveMethod = ApproveMethodType.RANDOM_SELECT_ONE_APPROVE
|
|
|
+ configForm.value.rejectHandlerType = RejectHandlerType.FINISH_PROCESS
|
|
|
+ notAllowedMultiApprovers.value = true
|
|
|
+ } else {
|
|
|
+ notAllowedMultiApprovers.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+// 审批方式改变
|
|
|
+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 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
|
|
|
+ const valid = await formRef.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.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!
|
|
|
+ // 解析候选人参数
|
|
|
+ parseCandidateParam(node.candidateStrategy!, node?.candidateParam)
|
|
|
+ if (configForm.value.userIds && configForm.value.userIds.length > 1) {
|
|
|
+ notAllowedMultiApprovers.value = true
|
|
|
+ } else {
|
|
|
+ notAllowedMultiApprovers.value = false
|
|
|
+ }
|
|
|
+ // 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) || DEFAULT_BUTTON_SETTING
|
|
|
+ // 4. 表单字段权限配置
|
|
|
+ getNodeConfigFormFields(node.fieldsPermission)
|
|
|
+}
|
|
|
+
|
|
|
+defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description 操作按钮设置
|
|
|
+ */
|
|
|
+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
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description 审批人超时未处理配置
|
|
|
+ */
|
|
|
+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
|
|
|
+ }
|
|
|
+}
|
|
|
+</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>
|