仿钉钉流程设计器- 新增审批类型

pull/452/head
jason 2024-08-17 22:49:04 +08:00
parent 996fe3329c
commit eb7e9397f5
6 changed files with 124 additions and 61 deletions

View File

@ -62,6 +62,8 @@ export interface SimpleFlowNode {
childNode?: SimpleFlowNode
// 条件节点
conditionNodes?: SimpleFlowNode[]
// 审批类型
approveType?: ApproveType
// 候选人策略
candidateStrategy?: number
// 候选人参数
@ -249,7 +251,23 @@ export enum AssignStartUserHandlerType {
/**
*
*/
ASSIGN_DEPT_LEADER
ASSIGN_DEPT_LEADER = 3
}
// 用户任务的审批类型。 【参考飞书】
export enum ApproveType {
/**
*
*/
USER = 1,
/**
*
*/
AUTO_APPROVE = 2,
/**
*
*/
AUTO_REJECT = 3
}
// 时间单位枚举
@ -285,13 +303,13 @@ export enum ConditionConfigType {
*
*/
export type ButtonSetting = {
id: OpsButtonType
id: OperationButtonType
displayName: string
enable: boolean
}
// 操作按钮类型枚举 (用于审批节点) // TODO @jason建议不缩写哈
export enum OpsButtonType {
// 操作按钮类型枚举 (用于审批节点)
export enum OperationButtonType {
/**
*
*/
@ -371,6 +389,12 @@ export const CANDIDATE_STRATEGY: DictDataVO[] = [
{ label: '用户组', value: CandidateStrategy.USER_GROUP },
{ label: '流程表达式', value: CandidateStrategy.EXPRESSION }
]
// 审批节点 的审批类型
export const APPROVE_TYPE: DictDataVO[] = [
{ label: '人工审批', value: ApproveType.USER },
{ label: '自动通过', value: ApproveType.AUTO_APPROVE },
{ label: '自动拒绝', value: ApproveType.AUTO_REJECT }
]
export const APPROVE_METHODS: DictDataVO[] = [
{ label: '随机挑选一人审批', value: ApproveMethodType.RRANDOM_SELECT_ONE_APPROVE },
@ -442,21 +466,21 @@ export const COMPARISON_OPERATORS: DictDataVO = [
]
// 审批操作按钮名称
export const OPERATION_BUTTON_NAME = new Map<number, string>()
OPERATION_BUTTON_NAME.set(OpsButtonType.APPROVE, '通过')
OPERATION_BUTTON_NAME.set(OpsButtonType.REJECT, '拒绝')
OPERATION_BUTTON_NAME.set(OpsButtonType.TRANSFER, '转办')
OPERATION_BUTTON_NAME.set(OpsButtonType.DELEGATE, '委派')
OPERATION_BUTTON_NAME.set(OpsButtonType.ADD_SIGN, '加签')
OPERATION_BUTTON_NAME.set(OpsButtonType.RETURN, '回退')
OPERATION_BUTTON_NAME.set(OperationButtonType.APPROVE, '通过')
OPERATION_BUTTON_NAME.set(OperationButtonType.REJECT, '拒绝')
OPERATION_BUTTON_NAME.set(OperationButtonType.TRANSFER, '转办')
OPERATION_BUTTON_NAME.set(OperationButtonType.DELEGATE, '委派')
OPERATION_BUTTON_NAME.set(OperationButtonType.ADD_SIGN, '加签')
OPERATION_BUTTON_NAME.set(OperationButtonType.RETURN, '回退')
// 默认的按钮权限设置
export const DEFAULT_BUTTON_SETTING: ButtonSetting[] = [
{ id: OpsButtonType.APPROVE, displayName: '通过', enable: true },
{ id: OpsButtonType.REJECT, displayName: '拒绝', enable: true },
{ id: OpsButtonType.TRANSFER, displayName: '转办', enable: false },
{ id: OpsButtonType.DELEGATE, displayName: '委派', enable: false },
{ id: OpsButtonType.ADD_SIGN, displayName: '加签', enable: false },
{ id: OpsButtonType.RETURN, displayName: '回退', enable: false }
{ id: OperationButtonType.APPROVE, displayName: '通过', enable: true },
{ id: OperationButtonType.REJECT, displayName: '拒绝', enable: true },
{ 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 MULTI_LEVEL_DEPT: DictDataVO = [

View File

@ -293,9 +293,9 @@ export function useNodeForm(nodeType: NodeType) {
break
// 指定连续多级部门的负责人
case CandidateStrategy.MULTI_LEVEL_DEPT_LEADER: {
// 候选人参数格式 ,分隔。 被分隔的最后一个为部门层级
// 候选人参数格式: | 分隔 。左边为部门(多个部门用 , 分隔)。 右边为部门层级
const deptIds = configForm.value.deptIds!.join(',')
candidateParam = deptIds.concat(',' + configForm.value.deptLevel + '')
candidateParam = deptIds.concat('|' + configForm.value.deptLevel + '')
break
}
default:
@ -341,13 +341,10 @@ export function useNodeForm(nodeType: NodeType) {
break
// 指定连续多级部门的负责人
case CandidateStrategy.MULTI_LEVEL_DEPT_LEADER: {
// 候选人参数格式 ,分隔。 被分隔的最后一个为部门层级
const paramArray = candidateParam.split(',')
configForm.value.deptIds = []
for (let i = 0; i < paramArray.length - 1; i++) {
configForm.value.deptIds.push(+paramArray[i])
}
configForm.value.deptLevel = +paramArray[paramArray.length - 1]
// 候选人参数格式: | 分隔 。左边为部门(多个部门用 , 分隔)。 右边为部门层级
const paramArray = candidateParam.split('|')
configForm.value.deptIds = paramArray[0].split(',').map((item) => +item)
configForm.value.deptLevel = +paramArray[1]
break
}
default:

View File

@ -181,7 +181,6 @@
</template>
<script setup lang="ts">
import { SimpleFlowNode, CandidateStrategy, NodeType, CANDIDATE_STRATEGY } from '../consts'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import {
useWatchNode,
useDrawer,
@ -261,7 +260,7 @@ const saveConfig = async () => {
const showText = getShowText()
if (!showText) return false
currentNode.value.name = nodeName.value!
handleCandidateParam()
currentNode.value.candidateParam = handleCandidateParam()
currentNode.value.candidateStrategy = configForm.value.candidateStrategy
currentNode.value.showText = showText
currentNode.value.fieldsPermission = fieldsPermissionConfig.value

View File

@ -24,7 +24,20 @@
<div class="divide-line"></div>
</div>
</template>
<el-tabs type="border-card" v-model="activeTabName">
<div class="flex flex-items-center mb-3">
<span class="font-size-4 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">
@ -58,7 +71,6 @@
/>
</el-select>
</el-form-item>
<!-- TODO @jason指定部门的选择不用联动父子例如说可能就是想某个比较高级别的部门审批 -->
<el-form-item
v-if="
configForm.candidateStrategy == CandidateStrategy.DEPT_MEMBER ||
@ -77,7 +89,7 @@
empty-text="加载中,请稍后"
multiple
node-key="id"
check-strictly
:check-strictly="true"
style="width: 100%"
show-checkbox
/>
@ -404,6 +416,8 @@
<script setup lang="ts">
import {
SimpleFlowNode,
APPROVE_TYPE,
ApproveType,
APPROVE_METHODS,
CandidateStrategy,
NodeType,
@ -434,7 +448,7 @@ import {
} from '../node'
import { defaultProps } from '@/utils/tree'
import { cloneDeep } from 'lodash-es'
import { convertTimeUnit } from '../utils'
import { convertTimeUnit, getApproveTypeText } from '../utils'
defineOptions({
name: 'UserTaskNodeConfig'
})
@ -469,7 +483,7 @@ const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFie
//
const { buttonsSetting, btnDisplayNameEdit, changeBtnDisplayName, btnDisplayNameBlurEvent } =
useButtonsSetting()
const approveType = ref(ApproveType.USER)
//
const formRef = ref() // Ref
//
@ -563,12 +577,23 @@ const {
//
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.name = nodeName.value!
currentNode.value.candidateStrategy = configForm.value.candidateStrategy
// candidateParam
currentNode.value.candidateParam = handleCandidateParam()
@ -612,7 +637,14 @@ const saveConfig = async () => {
//
const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
nodeName.value = node.name
//1.1
// 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)
@ -621,18 +653,18 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
} else {
notAllowedMultiApprovers.value = false
}
// 1.2
// 2.2
configForm.value.approveMethod = node.approveMethod!
if (node.approveMethod == ApproveMethodType.APPROVE_BY_RATIO) {
configForm.value.approveRatio = node.approveRatio!
}
// 1.3
// 2.3
configForm.value.rejectHandlerType = node.rejectHandler!.type
configForm.value.returnNodeId = node.rejectHandler?.returnNodeId
const matchNodeList = []
emits('find:returnTaskNodes', matchNodeList)
returnTaskList.value = matchNodeList
// 1.4
// 2.4
configForm.value.timeoutHandlerEnable = node.timeoutHandler!.enable
if (node.timeoutHandler?.enable && node.timeoutHandler?.timeDuration) {
const strTimeDuration = node.timeoutHandler.timeDuration
@ -643,14 +675,14 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
}
configForm.value.timeoutHandlerType = node.timeoutHandler?.type
configForm.value.maxRemindCount = node.timeoutHandler?.maxRemindCount
// 1.5
// 2.5
configForm.value.assignEmptyHandlerType = node.assignEmptyHandler?.type
configForm.value.assignEmptyHandlerUserIds = node.assignEmptyHandler?.userIds
// 1.6
// 2.6
configForm.value.assignStartUserHandlerType = node.assignStartUserHandlerType
// 2.
// 3.
buttonsSetting.value = cloneDeep(node.buttonsSetting) || DEFAULT_BUTTON_SETTING
// 3.
// 4.
getNodeConfigFormFields(node.fieldsPermission)
}

View File

@ -1,4 +1,4 @@
import { TimeUnitType } from './consts'
import { TimeUnitType, ApproveType, APPROVE_TYPE } from './consts'
// 获取条件节点默认的名称
export const getDefaultConditionNodeName = (index: number, defaultFlow: boolean): string => {
@ -20,3 +20,14 @@ export const convertTimeUnit = (strTimeUnit: string) => {
}
return TimeUnitType.HOUR
}
export const getApproveTypeText = (approveType: ApproveType): string => {
let approveTypeText = ''
APPROVE_TYPE.forEach((item) => {
if (item.value === approveType) {
approveTypeText = item.label
return
}
})
return approveTypeText
}

View File

@ -59,69 +59,69 @@
<!-- TODO @jason建议搞个 if 来判断替代现有的 !item.buttonsSetting || item.buttonsSetting[OpsButtonType.APPROVE]?.enable -->
<el-button
type="success"
v-if="!item.buttonsSetting || item.buttonsSetting[OpsButtonType.APPROVE]?.enable"
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.APPROVE]?.enable"
@click="handleAudit(item, true)"
>
<Icon icon="ep:select" />
<!-- TODO @jason这个也是类似哈搞个方法来生成名字 -->
{{
item.buttonsSetting?.[OpsButtonType.APPROVE]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.APPROVE)
item.buttonsSetting?.[OperationButtonType.APPROVE]?.displayName ||
OPERATION_BUTTON_NAME.get(OperationButtonType.APPROVE)
}}
</el-button>
<el-button
v-if="!item.buttonsSetting || item.buttonsSetting[OpsButtonType.REJECT]?.enable"
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.REJECT]?.enable"
type="danger"
@click="handleAudit(item, false)"
>
<Icon icon="ep:close" />
{{
item.buttonsSetting?.[OpsButtonType.REJECT].displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.REJECT)
item.buttonsSetting?.[OperationButtonType.REJECT].displayName ||
OPERATION_BUTTON_NAME.get(OperationButtonType.REJECT)
}}
</el-button>
<el-button
v-if="!item.buttonsSetting || item.buttonsSetting[OpsButtonType.TRANSFER]?.enable"
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.TRANSFER]?.enable"
type="primary"
@click="openTaskUpdateAssigneeForm(item.id)"
>
<Icon icon="ep:edit" />
{{
item.buttonsSetting?.[OpsButtonType.TRANSFER]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.TRANSFER)
item.buttonsSetting?.[OperationButtonType.TRANSFER]?.displayName ||
OPERATION_BUTTON_NAME.get(OperationButtonType.TRANSFER)
}}
</el-button>
<el-button
v-if="!item.buttonsSetting || item.buttonsSetting[OpsButtonType.DELEGATE]?.enable"
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.DELEGATE]?.enable"
type="primary"
@click="handleDelegate(item)"
>
<Icon icon="ep:position" />
{{
item.buttonsSetting?.[OpsButtonType.DELEGATE]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.DELEGATE)
item.buttonsSetting?.[OperationButtonType.DELEGATE]?.displayName ||
OPERATION_BUTTON_NAME.get(OperationButtonType.DELEGATE)
}}
</el-button>
<el-button
v-if="!item.buttonsSetting || item.buttonsSetting[OpsButtonType.ADD_SIGN]?.enable"
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.ADD_SIGN]?.enable"
type="primary"
@click="handleSign(item)"
>
<Icon icon="ep:plus" />
{{
item.buttonsSetting?.[OpsButtonType.ADD_SIGN]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.ADD_SIGN)
item.buttonsSetting?.[OperationButtonType.ADD_SIGN]?.displayName ||
OPERATION_BUTTON_NAME.get(OperationButtonType.ADD_SIGN)
}}
</el-button>
<el-button
v-if="!item.buttonsSetting || item.buttonsSetting[OpsButtonType.RETURN]?.enable"
v-if="!item.buttonsSetting || item.buttonsSetting[OperationButtonType.RETURN]?.enable"
type="warning"
@click="handleBack(item)"
>
<Icon icon="ep:back" />
{{
item.buttonsSetting?.[OpsButtonType.RETURN]?.displayName ||
OPERATION_BUTTON_NAME.get(OpsButtonType.RETURN)
item.buttonsSetting?.[OperationButtonType.RETURN]?.displayName ||
OPERATION_BUTTON_NAME.get(OperationButtonType.RETURN)
}}
</el-button>
</div>
@ -192,7 +192,7 @@ import { registerComponent } from '@/utils/routerHelper'
import { isEmpty } from '@/utils/is'
import * as UserApi from '@/api/system/user'
import {
OpsButtonType,
OperationButtonType,
OPERATION_BUTTON_NAME
} from '@/components/SimpleProcessDesignerV2/src/consts'