Pre Merge pull request !702 from Lemon/feature/bpm-流程审批-办理-抄送

pull/702/MERGE
Lemon 2025-02-18 06:44:57 +00:00 committed by Gitee
commit 20fdfd8081
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
24 changed files with 977 additions and 324 deletions

View File

@ -114,7 +114,8 @@ const include = [
'element-plus/es/components/segmented/style/css',
'@element-plus/icons-vue',
'element-plus/es/components/footer/style/css',
'element-plus/es/components/empty/style/css'
'element-plus/es/components/empty/style/css',
'element-plus/es/components/mention/style/css'
]
const exclude = ['@iconify/json']

View File

@ -0,0 +1 @@
<svg t="1739406626368" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1300" width="200" height="200"><path d="M803.221 925.573H224.356c-68.568 0-124.352-55.784-124.352-124.353V222.356c0-68.568 55.784-124.352 124.352-124.352h355.311v64H224.356c-33.278 0-60.352 27.074-60.352 60.352V801.22c0 33.278 27.074 60.353 60.352 60.353H803.22c33.278 0 60.353-27.074 60.353-60.353V448.208h64V801.22c0 68.569-55.784 124.353-124.352 124.353z" fill="#ffffff" p-id="1301"></path><path d="M300.357 756.916l35.024-195.867L770.117 84.404c10.05-11.02 25.015-18.052 41.058-19.293 16.017-1.247 31.987 3.379 43.841 12.667l83.662 65.549c21.643 16.956 24.254 45.964 5.942 66.038l-437.613 479.8-206.65 67.751z m104.994-170.751l-13.14 73.487 69.671-22.842 415.465-455.517-59.909-46.939-412.087 451.811z" fill="#ffffff" p-id="1302"></path><path d="M732.25 220.897l41.144-49.023 81.151 68.11-41.145 49.023z" fill="#ffffff" p-id="1303"></path></svg>

After

Width:  |  Height:  |  Size: 964 B

View File

@ -15,6 +15,12 @@
</div>
<div class="handler-item-text">审批人</div>
</div>
<div class="handler-item" @click="addNode(NodeType.TRANSACTOR_NODE)">
<div class="transactor handler-item-icon">
<span class="iconfont icon-transactor 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-icon copy">
<span class="iconfont icon-size icon-copy"></span>
@ -57,7 +63,7 @@
</div>
<div class="handler-item-text">触发器</div>
</div>
</div>
</div>
<template #reference>
<div class="add-icon"><Icon icon="ep:plus" /></div>
</template>
@ -78,7 +84,7 @@ import {
SimpleFlowNode,
DEFAULT_CONDITION_GROUP_VALUE
} from './consts'
import {generateUUID} from '@/utils'
import { generateUUID } from '@/utils'
defineOptions({
name: 'NodeHandler'
@ -114,13 +120,13 @@ const addNode = (type: number) => {
}
popoverShow.value = false
if (type === NodeType.USER_TASK_NODE) {
if (type === NodeType.USER_TASK_NODE || type === NodeType.TRANSACTOR_NODE) {
const id = 'Activity_' + generateUUID()
const data: SimpleFlowNode = {
id: id,
name: NODE_DEFAULT_NAME.get(NodeType.USER_TASK_NODE) as string,
name: NODE_DEFAULT_NAME.get(type) as string,
showText: '',
type: NodeType.USER_TASK_NODE,
type: type,
approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE,
//
rejectHandler: {

View File

@ -6,7 +6,11 @@
/>
<!-- 审批节点 -->
<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"
@update:flow-node="handleModelValueUpdate"
@find:parent-node="findFromParentNode"

View File

@ -45,4 +45,3 @@ watch(
provide('tasks', approveTasks)
provide('processInstance', currentProcessInstance)
</script>
p

View File

@ -23,6 +23,11 @@ export enum NodeType {
*/
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.ROUTER_BRANCH_NODE, '请设置路由节点')
NODE_DEFAULT_TEXT.set(NodeType.TRIGGER_NODE, '请设置触发器')
NODE_DEFAULT_TEXT.set(NodeType.TRANSACTOR_NODE, '请设置办理人')
export const NODE_DEFAULT_NAME = new Map<number, string>()
NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
@ -515,15 +521,17 @@ NODE_DEFAULT_NAME.set(NodeType.START_USER_NODE, '发起人')
NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器')
NODE_DEFAULT_NAME.set(NodeType.ROUTER_BRANCH_NODE, '路由分支')
NODE_DEFAULT_NAME.set(NodeType.TRIGGER_NODE, '触发器')
NODE_DEFAULT_NAME.set(NodeType.TRANSACTOR_NODE, '办理人')
// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
export const CANDIDATE_STRATEGY: DictDataVO[] = [
{ label: '指定成员', value: CandidateStrategy.USER },
{ label: '指定角色', value: CandidateStrategy.ROLE },
{ label: '指定岗位', value: CandidateStrategy.POST },
{ label: '部门成员', value: CandidateStrategy.DEPT_MEMBER },
{ label: '部门负责人', value: CandidateStrategy.DEPT_LEADER },
{ label: '连续多级部门负责人', value: CandidateStrategy.MULTI_LEVEL_DEPT_LEADER },
{ label: '发起人自选', value: CandidateStrategy.START_USER_SELECT },
{ label: '指定岗位', value: CandidateStrategy.MULTI_LEVEL_DEPT_LEADER },
{ label: '发起人本人', value: CandidateStrategy.START_USER },
{ label: '发起人部门负责人', value: CandidateStrategy.START_USER_DEPT_LEADER },
{ label: '发起人连续部门负责人', value: CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER },
@ -627,6 +635,16 @@ export const DEFAULT_BUTTON_SETTING: ButtonSetting[] = [
{ 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[] = [
{ id: OperationButtonType.APPROVE, displayName: '提交', enable: true },
@ -717,7 +735,7 @@ export type RouterSetting = {
export type TriggerSetting = {
type: TriggerTypeEnum
httpRequestSetting?: HttpRequestTriggerSetting
normalFormSetting?: NormalFormTriggerSetting
formSettings?: FormTriggerSetting[]
}
/**
@ -751,7 +769,13 @@ export type HttpRequestTriggerSetting = {
/**
*
*/
export type NormalFormTriggerSetting = {
export type FormTriggerSetting = {
// 条件类型
conditionType?: ConditionType
// 条件表达式
conditionExpression?: string
// 条件组
conditionGroups?: ConditionGroup
// 更新表单字段
updateFormFields?: Record<string, any>
}

View File

@ -15,7 +15,10 @@ import {
AssignEmptyHandlerType,
FieldPermissionType,
HttpRequestParam,
ProcessVariableEnum
ProcessVariableEnum,
ConditionType,
ConditionGroup,
COMPARISON_OPERATORS
} from './consts'
import { parseFormFields } from '@/components/FormCreate/src/utils'
@ -201,7 +204,7 @@ export function useNodeForm(nodeType: NodeType) {
const deptTreeOptions = inject('deptTree', ref()) // 部门树
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
const configForm = ref<UserTaskFormType | CopyTaskFormType>()
if (nodeType === NodeType.USER_TASK_NODE) {
if (nodeType === NodeType.USER_TASK_NODE || nodeType === NodeType.TRANSACTOR_NODE) {
configForm.value = {
candidateStrategy: CandidateStrategy.USER,
approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE,
@ -543,6 +546,66 @@ export function useTaskStatusClass(taskStatus: TaskStatusEnum | undefined): stri
if (taskStatus === TaskStatusEnum.CANCEL) {
return 'status-cancel'
}
return ''
}
/** 条件组件文字展示 */
export function getConditionShowText(
conditonType: ConditionType | undefined,
conditionExpression: string | undefined,
conditionGroups: ConditionGroup | undefined,
fieldOptions: Array<Record<string, any>>
) {
let showText = ''
if (conditonType === ConditionType.EXPRESSION) {
if (conditionExpression) {
showText = `表达式:${conditionExpression}`
}
}
if (conditonType === ConditionType.RULE) {
// 条件组是否为与关系
const groupAnd = conditionGroups?.and
let warningMesg: undefined | string = undefined
const conditionGroup = conditionGroups?.conditions.map((item) => {
return (
'(' +
item.rules
.map((rule) => {
if (rule.leftSide && rule.rightSide) {
return (
getFormFieldTitle(fieldOptions, rule.leftSide) +
' ' +
getOpName(rule.opCode) +
' ' +
rule.rightSide
)
} else {
// 有一条规则不完善。提示错误
warningMesg = '请完善条件规则'
return ''
}
})
.join(item.and ? ' 且 ' : ' 或 ') +
' ) '
)
})
if (warningMesg) {
showText = ''
} else {
showText = conditionGroup!.join(groupAnd ? ' 且 ' : ' 或 ')
}
}
return showText
}
/** 获取表单字段名称*/
const getFormFieldTitle = (fieldOptions: Array<Record<string, any>>, field: string) => {
const item = fieldOptions.find((item) => item.field === field)
return item?.title
}
/** 获取操作符名称 */
const getOpName = (opCode: string): string => {
const opName = COMPARISON_OPERATORS.find((item: any) => item.value === opCode)
return opName?.label
}

View File

@ -43,13 +43,9 @@
</el-drawer>
</template>
<script setup lang="ts">
import {
SimpleFlowNode,
ConditionType,
COMPARISON_OPERATORS,
} from '../consts'
import { SimpleFlowNode, ConditionType } from '../consts'
import { getDefaultConditionNodeName } from '../utils'
import { useFormFieldsAndStartUser } from '../node'
import { useFormFieldsAndStartUser, getConditionShowText } from '../node'
import Condition from './components/Condition.vue'
const message = useMessage() //
defineOptions({
@ -93,8 +89,6 @@ const blurEvent = () => {
getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.conditionSetting?.defaultFlow)
}
defineExpose({ open }) // open
//
@ -110,6 +104,8 @@ const handleClose = async (done: (cancel?: boolean) => void) => {
done()
}
}
//
const fieldOptions = useFormFieldsAndStartUser()
const conditionRef = ref()
//
@ -118,7 +114,12 @@ const saveConfig = async () => {
//
const valid = await conditionRef.value.validate()
if (!valid) return false
const showText = getShowText()
const showText = getConditionShowText(
condition.value?.conditionType,
condition.value?.conditionExpression,
condition.value.conditionGroups,
fieldOptions
)
if (!showText) {
return false
}
@ -136,59 +137,6 @@ const saveConfig = async () => {
settingVisible.value = false
return true
}
const getShowText = (): string => {
let showText = ''
if (condition.value?.conditionType === ConditionType.EXPRESSION) {
if (condition.value.conditionExpression) {
showText = `表达式:${condition.value.conditionExpression}`
}
}
if (condition.value?.conditionType === ConditionType.RULE) {
//
const groupAnd = condition.value.conditionGroups?.and
let warningMesg: undefined | string = undefined
const conditionGroup = condition.value.conditionGroups?.conditions.map((item) => {
return (
'(' +
item.rules
.map((rule) => {
if (rule.leftSide && rule.rightSide) {
return (
getFieldTitle(rule.leftSide) + ' ' + getOpName(rule.opCode) + ' ' + rule.rightSide
)
} else {
//
warningMesg = '请完善条件规则'
return ''
}
})
.join(item.and ? ' 且 ' : ' 或 ') +
' ) '
)
})
if (warningMesg) {
message.warning(warningMesg)
showText = ''
} else {
showText = conditionGroup!.join(groupAnd ? ' 且 ' : ' 或 ')
}
}
return showText
}
//
const fieldOptions = useFormFieldsAndStartUser()
/** 获取字段名称 */
const getFieldTitle = (field: string) => {
const item = fieldOptions.find((item) => item.field === field)
return item?.title
}
/** 获取操作符名称 */
const getOpName = (opCode: string): string => {
const opName = COMPARISON_OPERATORS.find((item: any) => item.value === opCode)
return opName?.label
}
</script>
<style lang="scss" scoped>

View File

@ -195,9 +195,13 @@
<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">只读
<el-switch v-model="readSwitch" @change="updatePermission('READ')" />
</span>
<span class="setting-title-label">可编辑</span>
<span class="setting-title-label">隐藏</span>
<span class="setting-title-label">隐藏
<el-switch v-model="noneSwitch" @change="updatePermission('NONE')" />
</span>
</div>
</div>
<div
@ -367,7 +371,23 @@ const showCopyTaskNodeConfig = (node: SimpleFlowNode) => {
//
getNodeConfigFormFields(node.fieldsPermission)
}
const readSwitch = ref(false);
const noneSwitch = ref(false);
const updatePermission = (type: string) => {
if (type === 'READ') {
readSwitch.value = true;
noneSwitch.value = false;
} else if (type === 'NONE') {
readSwitch.value = false;
noneSwitch.value = true;
}
fieldsPermissionConfig.value.forEach(field => {
field.permission = type === 'READ' ? FieldPermissionType.READ : FieldPermissionType.NONE;
});
};
defineExpose({ openDrawer, showCopyTaskNodeConfig }) //
</script>

View File

@ -46,9 +46,15 @@
<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>
<span class="setting-title-label">只读
<el-switch v-model="readSwitch" @change="updatePermission('READ')" />
</span>
<span class="setting-title-label">可编辑
<el-switch v-model="writeSwitch" @change="updatePermission('WRITE')" />
</span>
<span class="setting-title-label">隐藏
<el-switch v-model="noneSwitch" @change="updatePermission('NONE')" />
</span>
</div>
</div>
<div
@ -156,8 +162,32 @@ const showStartUserNodeConfig = (node: SimpleFlowNode) => {
//
getNodeConfigFormFields(node.fieldsPermission)
}
const readSwitch = ref(false)
const writeSwitch = ref(false)
const noneSwitch = ref(false)
const updatePermission = (type) => {
if (type === 'READ') {
readSwitch.value = true;
writeSwitch.value = false;
noneSwitch.value = false;
} else if (type === 'WRITE') {
readSwitch.value = false;
writeSwitch.value = true;
noneSwitch.value = false;
} else if (type === 'NONE') {
readSwitch.value = false;
writeSwitch.value = false;
noneSwitch.value = true;
}
fieldsPermissionConfig.value.forEach(item => {
item.permission = type === 'READ' ? FieldPermissionType.READ :
type === 'WRITE' ? FieldPermissionType.WRITE :
FieldPermissionType.NONE;
});
}
defineExpose({ openDrawer, showStartUserNodeConfig }) //
</script>
<style lang="scss" scoped></style>

View File

@ -3,7 +3,7 @@
:append-to-body="true"
v-model="settingVisible"
:show-close="false"
:size="550"
:size="630"
:before-close="saveConfig"
>
<template #header>
@ -121,62 +121,114 @@
</el-button>
</el-form-item>
</div>
<div
v-if="
configForm.type === TriggerTypeEnum.UPDATE_NORMAL_FORM && configForm.normalFormSetting
"
>
<el-divider content-position="left">修改表单设置</el-divider>
<div
class="flex items-center"
v-for="key in Object.keys(configForm.normalFormSetting.updateFormFields!)"
:key="key"
>
<div class="mr-2 flex items-center">
<el-form-item>
<el-select
class="w-160px!"
:model-value="key"
@update:model-value="(newKey) => updateFormFieldKey(key, newKey)"
placeholder="请选择表单字段"
:disabled="key !== ''"
<!-- 表单数据修改触发器 -->
<div v-if="configForm.type === TriggerTypeEnum.UPDATE_NORMAL_FORM">
<div v-for="(formSetting, index) in configForm.formSettings" :key="index">
<el-card class="w-580px mt-4">
<template #header>
<div class="flex items-center justify-between">
<div>修改表单设置 {{ index + 1 }}</div>
<el-button
type="primary"
plain
circle
v-if="configForm.formSettings!.length > 1"
@click="deleteFormSetting(index)"
>
<Icon icon="ep:close" />
</el-button>
</div>
</template>
<!-- 条件设置 -->
<ConditionDialog
:ref="`condition-${index}`"
@update-condition="(val) => handleConditionUpdate(index, val)"
/>
<div class="cursor-pointer" v-if="formSetting.conditionType">
<el-tag
type="success"
effect="light"
closable
@close="deleteFormUpdateCondition(formSetting)"
@click="openFormUpdateCondition(index, formSetting)"
>
<el-option
v-for="(field, fIdx) in optionalUpdateFormFields"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="field.disabled"
/>
</el-select>
</el-form-item>
</div>
<div class="mx-2"><el-form-item>的值设置为</el-form-item></div>
<div class="mr-2">
<el-form-item
:prop="`normalFormSetting.updateFormFields.${key}`"
:rules="{
required: true,
message: '值不能为空',
trigger: 'blur'
}"
{{ showConditionText(formSetting) }}
</el-tag>
</div>
<el-button
v-else
type="primary"
text
@click="addFormUpdateCondition(index, formSetting)"
>
<el-input
class="w-160px"
v-model="configForm.normalFormSetting.updateFormFields![key]"
placeholder="请输入"
:disabled="!key"
/>
</el-form-item>
</div>
<div class="mr-1 pt-1 cursor-pointer">
<el-form-item>
<Icon icon="ep:delete" :size="18" @click="deleteFormFieldSetting(key)" />
</el-form-item>
</div>
<Icon icon="ep:link" class="mr-5px" />添加条件
</el-button>
<el-divider content-position="left">修改表单字段设置</el-divider>
<!-- 表单字段修改设置 -->
<div
class="flex items-center"
v-for="key in Object.keys(formSetting.updateFormFields || {})"
:key="key"
>
<div class="mr-2 flex items-center">
<el-form-item>
<el-select
class="w-160px!"
:model-value="key"
@update:model-value="(newKey) => updateFormFieldKey(formSetting, key, newKey)"
placeholder="请选择表单字段"
:disabled="key !== ''"
>
<el-option
v-for="(field, fIdx) in optionalUpdateFormFields"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="field.disabled"
/>
</el-select>
</el-form-item>
</div>
<div class="mx-2"><el-form-item>的值设置为</el-form-item></div>
<div class="mr-2">
<el-form-item
:prop="`formSettings.${index}.updateFormFields.${key}`"
:rules="{
required: true,
message: '值不能为空',
trigger: 'blur'
}"
>
<el-input
class="w-160px"
v-model="formSetting.updateFormFields![key]"
placeholder="请输入"
:disabled="!key"
/>
</el-form-item>
</div>
<div class="mr-1 pt-1 cursor-pointer">
<el-form-item>
<Icon
icon="ep:delete"
:size="18"
@click="deleteFormFieldSetting(formSetting, key)"
/>
</el-form-item>
</div>
</div>
<!-- 添加表单字段按钮 -->
<el-button type="primary" text @click="addFormFieldSetting(formSetting)">
<Icon icon="ep:memo" class="mr-5px" />添加修改字段
</el-button>
</el-card>
</div>
<el-button type="primary" text @click="addFormFieldSetting()">
<Icon icon="ep:plus" class="mr-5px" />添加修改字段
<!-- 添加新的设置 -->
<el-button class="mt-6" type="primary" text @click="addFormSetting">
<Icon icon="ep:setting" class="mr-5px" />添加设置
</el-button>
</div>
</el-form>
@ -191,10 +243,26 @@
</el-drawer>
</template>
<script setup lang="ts">
import { SimpleFlowNode, NodeType, TriggerSetting, TRIGGER_TYPES, TriggerTypeEnum } from '../consts'
import { useWatchNode, useDrawer, useNodeName, useFormFields } from '../node'
import {
SimpleFlowNode,
NodeType,
TriggerSetting,
TRIGGER_TYPES,
TriggerTypeEnum,
FormTriggerSetting,
DEFAULT_CONDITION_GROUP_VALUE
} from '../consts'
import {
useWatchNode,
useDrawer,
useNodeName,
useFormFields,
useFormFieldsAndStartUser,
getConditionShowText
} from '../node'
import HttpRequestParamSetting from './components/HttpRequestParamSetting.vue'
import ConditionDialog from './components/ConditionDialog.vue'
const { proxy } = getCurrentInstance() as any
defineOptions({
name: 'TriggerNodeConfig'
})
@ -227,28 +295,25 @@ const configForm = ref<TriggerSetting>({
body: [],
response: []
},
normalFormSetting: { updateFormFields: {} }
formSettings: [
{
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
updateFormFields: {}
}
]
})
//
const formFields = useFormFields()
//
const optionalUpdateFormFields = computed(() => {
const usedFields = Object.keys(configForm.value.normalFormSetting?.updateFormFields || {})
return formFields.map((field) => ({
title: field.title,
field: field.field,
disabled: usedFields.includes(field.field)
disabled: false
}))
})
const updateFormFieldKey = (oldKey: string, newKey: string) => {
if (!configForm.value.normalFormSetting?.updateFormFields) return
const value = configForm.value.normalFormSetting.updateFormFields[oldKey]
delete configForm.value.normalFormSetting.updateFormFields[oldKey]
configForm.value.normalFormSetting.updateFormFields[newKey] = value
}
/** 添加 HTTP 请求返回值设置项*/
const addHttpResponseSetting = (responseSetting: Record<string, string>[]) => {
responseSetting.push({
@ -262,17 +327,69 @@ const deleteHttpResponseSetting = (responseSetting: Record<string, string>[], in
responseSetting.splice(index, 1)
}
/** 添加修改表单设置 */
const addFormFieldSetting = () => {
if (configForm.value.normalFormSetting!.updateFormFields === undefined) {
configForm.value.normalFormSetting!.updateFormFields = {}
}
configForm.value.normalFormSetting!.updateFormFields[''] = undefined
/** 添加新的修改表单设置 */
const addFormSetting = () => {
configForm.value.formSettings!.push({
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
updateFormFields: {}
})
}
/** 删除修改表单设置项 */
const deleteFormFieldSetting = (key: string) => {
if (!configForm.value.normalFormSetting?.updateFormFields) return
delete configForm.value.normalFormSetting.updateFormFields[key]
/** 删除修改表单设置 */
const deleteFormSetting = (index: number) => {
configForm.value.formSettings!.splice(index, 1)
}
/** 添加条件配置 */
const addFormUpdateCondition = (index: number, formSetting: FormTriggerSetting) => {
const conditionDialog = proxy.$refs[`condition-${index}`][0]
conditionDialog.open(formSetting)
}
/** 删除条件配置 */
const deleteFormUpdateCondition = (formSetting: FormTriggerSetting) => {
formSetting.conditionType = undefined
}
/** 打开条件配置弹窗 */
const openFormUpdateCondition = (index: number, formSetting: FormTriggerSetting) => {
const conditionDialog = proxy.$refs[`condition-${index}`][0]
conditionDialog.open(formSetting)
}
/** 处理条件配置保存 */
const handleConditionUpdate = (index: number, condition: any) => {
configForm.value.formSettings![index].conditionType = condition.conditionType
configForm.value.formSettings![index].conditionExpression = condition.conditionExpression
configForm.value.formSettings![index].conditionGroups = condition.conditionGroups
}
/** 条件配置展示 */
const showConditionText = (formSetting: FormTriggerSetting) => {
return getConditionShowText(
formSetting.conditionType,
formSetting.conditionExpression,
formSetting.conditionGroups,
formFields
)
}
/** 添加修改字段设置项 */
const addFormFieldSetting = (formSetting: FormTriggerSetting) => {
if (!formSetting) return
if (!formSetting.updateFormFields) {
formSetting.updateFormFields = {}
}
formSetting.updateFormFields[''] = undefined
}
/** 更新字段 KEY */
const updateFormFieldKey = (formSetting: FormTriggerSetting, oldKey: string, newKey: string) => {
if (!formSetting?.updateFormFields) return
const value = formSetting.updateFormFields[oldKey]
delete formSetting.updateFormFields[oldKey]
formSetting.updateFormFields[newKey] = value
}
/** 删除修改字段设置项 */
const deleteFormFieldSetting = (formSetting: FormTriggerSetting, key: string) => {
if (!formSetting?.updateFormFields) return
delete formSetting.updateFormFields[key]
}
/** 保存配置 */
@ -285,7 +402,7 @@ const saveConfig = async () => {
currentNode.value.name = nodeName.value!
currentNode.value.showText = showText
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
configForm.value.normalFormSetting = undefined
configForm.value.formSettings = undefined
}
if (configForm.value.type === TriggerTypeEnum.UPDATE_NORMAL_FORM) {
configForm.value.httpRequestSetting = undefined
@ -301,12 +418,13 @@ const getShowText = (): string => {
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
showText = `${configForm.value.httpRequestSetting?.url}`
} else if (configForm.value.type === TriggerTypeEnum.UPDATE_NORMAL_FORM) {
const updatefields = Object.keys(configForm.value.normalFormSetting?.updateFormFields || {})
if (updatefields.length === 0) {
message.warning('请设置修改表单字段')
} else {
showText = '修改表单数据'
for (const [index, setting] of configForm.value.formSettings!.entries()) {
if (!setting.updateFormFields || Object.keys(setting.updateFormFields).length === 0) {
message.warning(`请添加表单设置${index + 1}的修改字段`)
return ''
}
}
showText = '修改表单数据'
}
return showText
}
@ -323,7 +441,12 @@ const showTriggerNodeConfig = (node: SimpleFlowNode) => {
body: [],
response: []
},
normalFormSetting: node.triggerSetting.normalFormSetting || { updateFormFields: {} }
formSettings: node.triggerSetting.formSettings || [
{
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE,
updateFormFields: {}
}
]
}
}
}

View File

@ -25,7 +25,7 @@
<div class="divide-line"></div>
</div>
</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>
<el-radio-group v-model="approveType">
<el-radio
@ -39,10 +39,10 @@
</el-radio-group>
</div>
<el-tabs type="border-card" v-model="activeTabName" v-if="approveType === ApproveType.USER">
<el-tab-pane label="审批人" name="user">
<el-tab-pane :label="`${nodeTypeName}人`" name="user">
<div>
<el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
<el-form-item label="审批人设置" prop="candidateStrategy">
<el-form-item :label="`${nodeTypeName}人设置`" prop="candidateStrategy">
<el-radio-group
v-model="configForm.candidateStrategy"
@change="changeCandidateStrategy"
@ -61,7 +61,13 @@
label="指定角色"
prop="roleIds"
>
<el-select filterable v-model="configForm.roleIds" clearable multiple style="width: 100%">
<el-select
filterable
v-model="configForm.roleIds"
clearable
multiple
style="width: 100%"
>
<el-option
v-for="item in roleOptions"
:key="item.id"
@ -99,7 +105,13 @@
prop="postIds"
span="24"
>
<el-select filterable v-model="configForm.postIds" clearable multiple style="width: 100%">
<el-select
filterable
v-model="configForm.postIds"
clearable
multiple
style="width: 100%"
>
<el-option
v-for="item in postOptions"
:key="item.id"
@ -114,7 +126,13 @@
prop="userIds"
span="24"
>
<el-select filterable v-model="configForm.userIds" clearable multiple style="width: 100%">
<el-select
filterable
v-model="configForm.userIds"
clearable
multiple
style="width: 100%"
>
<el-option
v-for="item in userOptions"
:key="item.id"
@ -128,7 +146,13 @@
label="指定用户组"
prop="userGroups"
>
<el-select filterable v-model="configForm.userGroups" clearable multiple style="width: 100%">
<el-select
filterable
v-model="configForm.userGroups"
clearable
multiple
style="width: 100%"
>
<el-option
v-for="item in userGroupOptions"
:key="item.id"
@ -201,7 +225,7 @@
style="width: 100%"
/>
</el-form-item>
<el-form-item label="多人审批方式" prop="approveMethod">
<el-form-item :label="`多人${nodeTypeName}方式`" prop="approveMethod">
<el-radio-group v-model="configForm.approveMethod" @change="approveMethodChanged">
<div class="flex-col">
<div
@ -230,93 +254,102 @@
</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 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>
</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>
<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-group>
</el-form-item>
<el-form-item
v-if="configForm.rejectHandlerType == RejectHandlerType.RETURN_USER_TASK"
label="驳回节点"
prop="returnNodeId"
>
<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-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-select
filterable
v-model="timeUnit"
class="mr-2"
:style="{ width: '100px' }"
@change="timeUnitChange"
<el-form-item
label="执行动作"
prop="timeoutHandlerType"
v-if="configForm.timeoutHandlerEnable"
>
<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-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">审批人为空时</el-divider>
<el-divider content-position="left">{{ nodeTypeName }}人为空时</el-divider>
<el-form-item prop="assignEmptyHandlerType">
<el-radio-group v-model="configForm.assignEmptyHandlerType">
<div class="flex-col">
@ -348,30 +381,44 @@
</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 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>
</div>
</el-radio-group>
</el-form-item>
</el-radio-group>
</el-form-item>
</div>
<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 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>
<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 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>
</el-form>
</div>
</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-desc">操作按钮</div>
<div class="button-setting-title">
@ -407,9 +454,15 @@
<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>
<span class="setting-title-label">只读
<el-switch v-model="readSwitch" @change="updatePermission('READ')" />
</span>
<span class="setting-title-label">可编辑
<el-switch v-model="writeSwitch" @change="updatePermission('WRITE')" />
</span>
<span class="setting-title-label">隐藏
<el-switch v-model="noneSwitch" @change="updatePermission('NONE')" />
</span>
</div>
</div>
<div
@ -448,7 +501,11 @@
</div>
</el-tab-pane>
<el-tab-pane label="监听器" name="listener">
<UserTaskListener ref="userTaskListenerRef" v-model="configForm" :form-field-options="formFieldOptions" />
<UserTaskListener
ref="userTaskListenerRef"
v-model="configForm"
:form-field-options="formFieldOptions"
/>
</el-tab-pane>
</el-tabs>
<template #footer>
@ -485,7 +542,8 @@ import {
ASSIGN_EMPTY_HANDLER_TYPES,
AssignEmptyHandlerType,
FieldPermissionType,
ProcessVariableEnum
ProcessVariableEnum,
TRANSACTOR_DEFAULT_BUTTON_SETTING
} from '../consts'
import {
@ -588,7 +646,7 @@ const {
handleCandidateParam,
parseCandidateParam,
getShowText
} = useNodeForm(NodeType.USER_TASK_NODE)
} = useNodeForm(currentNode.value.type)
const configForm = tempConfigForm as Ref<UserTaskFormType>
//
@ -627,7 +685,12 @@ const {
const userTaskListenerRef = ref()
//
/** 节点类型名称 */
const nodeTypeName = computed(() => {
return currentNode.value.type === NodeType.TRANSACTOR_NODE ? '办理' : '审批'
})
/** 保存配置 */
const saveConfig = async () => {
// activeTabName.value = 'user'
//
@ -713,7 +776,7 @@ const saveConfig = async () => {
return true
}
//
/** 显示审批节点配置, 由父组件传过来 */
const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
nodeName.value = node.name
// 1
@ -733,13 +796,13 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
configForm.value.approveRatio = node.approveRatio!
}
// 2.3
configForm.value.rejectHandlerType = node.rejectHandler!.type
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
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)
@ -755,7 +818,11 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
// 2.6
configForm.value.assignStartUserHandlerType = node.assignStartUserHandlerType
// 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.
getNodeConfigFormFields(node.fieldsPermission)
// 5.
@ -773,7 +840,7 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
header: node.taskAssignListener?.header ?? [],
body: node.taskAssignListener?.body ?? []
}
// 5.3
// 5.3
configForm.value.taskCompleteListenerEnable = node.taskCompleteListener?.enable
configForm.value.taskCompleteListenerPath = node.taskCompleteListener?.path
configForm.value.taskCompleteListener = {
@ -788,9 +855,7 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
defineExpose({ openDrawer, showUserTaskNodeConfig }) //
/**
* @description 操作按钮设置
*/
/** 操作按钮设置 */
function useButtonsSetting() {
const buttonsSetting = ref<ButtonSetting[]>()
//
@ -811,9 +876,7 @@ function useButtonsSetting() {
}
}
/**
* @description 审批人超时未处理配置
*/
/** 审批人超时未处理配置 */
function useTimeoutHandler() {
//
const timeUnit = ref(TimeUnitType.HOUR)
@ -896,6 +959,31 @@ function useTimeoutHandler() {
cTimeoutMaxRemindCount
}
}
const readSwitch = ref(false);
const writeSwitch = ref(false);
const noneSwitch = ref(false);
const updatePermission = (type: string) => {
if (type === 'READ') {
readSwitch.value = true;
writeSwitch.value = false;
noneSwitch.value = false;
} else if (type === 'WRITE') {
readSwitch.value = false;
writeSwitch.value = true;
noneSwitch.value = false;
} else if (type === 'NONE') {
readSwitch.value = false;
writeSwitch.value = false;
noneSwitch.value = true;
}
fieldsPermissionConfig.value.forEach(field => {
field.permission = type === 'READ' ? FieldPermissionType.READ :
type === 'WRITE' ? FieldPermissionType.WRITE :
FieldPermissionType.NONE;
});
};
</script>
<style lang="scss" scoped>

View File

@ -0,0 +1,303 @@
<template>
<Dialog v-model="dialogVisible" title="条件配置" width="600px" :fullscreen="false">
<div class="h-410px">
<el-scrollbar wrap-class="h-full">
<el-form ref="formRef" :model="condition" :rules="formRules" label-position="top">
<el-form-item label="配置方式" prop="conditionType">
<el-radio-group v-model="condition.conditionType" @change="changeConditionType">
<el-radio
v-for="(dict, indexConditionType) in conditionConfigTypes"
:key="indexConditionType"
:value="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="condition.conditionType === ConditionType.RULE && condition.conditionGroups"
label="条件规则"
>
<div class="condition-group-tool">
<div class="flex items-center">
<div class="mr-4">条件组关系</div>
<el-switch
v-model="condition.conditionGroups.and"
inline-prompt
active-text="且"
inactive-text="或"
/>
</div>
</div>
<el-space direction="vertical" :spacer="condition.conditionGroups.and ? '且' : '或'">
<el-card
class="condition-group"
style="width: 530px"
v-for="(equation, cIdx) in condition.conditionGroups.conditions"
:key="cIdx"
>
<div
class="condition-group-delete"
v-if="condition.conditionGroups.conditions.length > 1"
>
<Icon
color="#0089ff"
icon="ep:circle-close-filled"
:size="18"
@click="deleteConditionGroup(condition.conditionGroups.conditions, cIdx)"
/>
</div>
<template #header>
<div class="flex items-center justify-between">
<div>条件组</div>
<div class="flex">
<div class="mr-4">规则关系</div>
<el-switch
v-model="equation.and"
inline-prompt
active-text="且"
inactive-text="或"
/>
</div>
</div>
</template>
<div class="flex pt-2" v-for="(rule, rIdx) in equation.rules" :key="rIdx">
<div class="mr-2">
<el-form-item
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.leftSide`"
:rules="{
required: true,
message: '左值不能为空',
trigger: 'change'
}"
>
<el-select style="width: 160px" v-model="rule.leftSide">
<el-option
v-for="(field, fIdx) in fieldOptions"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="!field.required"
/>
</el-select>
</el-form-item>
</div>
<div class="mr-2">
<el-select v-model="rule.opCode" style="width: 100px">
<el-option
v-for="operator in COMPARISON_OPERATORS"
:key="operator.value"
:label="operator.label"
:value="operator.value"
/>
</el-select>
</div>
<div class="mr-2">
<el-form-item
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.rightSide`"
:rules="{
required: true,
message: '右值不能为空',
trigger: 'blur'
}"
>
<el-input v-model="rule.rightSide" style="width: 160px" />
</el-form-item>
</div>
<div class="cursor-pointer mr-1 flex items-center" v-if="equation.rules.length > 1">
<Icon
icon="ep:delete"
:size="18"
@click="deleteConditionRule(equation, rIdx)"
/>
</div>
<div class="cursor-pointer flex items-center">
<Icon icon="ep:plus" :size="18" @click="addConditionRule(equation, rIdx)" />
</div>
</div>
</el-card>
</el-space>
<div title="添加条件组" class="mt-4 cursor-pointer">
<Icon
color="#0089ff"
icon="ep:plus"
:size="24"
@click="addConditionGroup(condition.conditionGroups?.conditions)"
/>
</div>
</el-form-item>
<el-form-item
v-if="condition.conditionType === ConditionType.EXPRESSION"
label="条件表达式"
prop="conditionExpression"
>
<el-input
type="textarea"
v-model="condition.conditionExpression"
clearable
style="width: 100%"
/>
</el-form-item>
</el-form>
</el-scrollbar>
</div>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import {
COMPARISON_OPERATORS,
CONDITION_CONFIG_TYPES,
ConditionType,
ConditionGroup,
DEFAULT_CONDITION_GROUP_VALUE
} from '../../consts'
import { BpmModelFormType } from '@/utils/constants'
import { useFormFieldsAndStartUser } from '../../node'
defineOptions({
name: 'ConditionDialog'
})
const condition = ref<{
conditionType: ConditionType
conditionExpression?: string
conditionGroups?: ConditionGroup
}>({
conditionType: ConditionType.RULE,
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
})
const emit = defineEmits<{
updateCondition: [condition: object]
}>()
const message = useMessage() //
const dialogVisible = ref(false) //
const formType = inject<Ref<number>>('formType') //
const conditionConfigTypes = computed(() => {
return CONDITION_CONFIG_TYPES.filter((item) => {
//
if (formType?.value === BpmModelFormType.CUSTOM && item.value === ConditionType.RULE) {
return false
} else {
return true
}
})
})
/** 条件规则可选择的表单字段 */
const fieldOptions = useFormFieldsAndStartUser()
//
const formRules = reactive({
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
})
const formRef = ref() // Ref
/** 切换条件配置方式 */
const changeConditionType = () => {
if (condition.value.conditionType === ConditionType.RULE) {
if (!condition.value.conditionGroups) {
condition.value.conditionGroups = DEFAULT_CONDITION_GROUP_VALUE
}
}
}
const deleteConditionGroup = (conditions, index) => {
conditions.splice(index, 1)
}
const deleteConditionRule = (condition, index) => {
condition.rules.splice(index, 1)
}
const addConditionRule = (condition, index) => {
const rule = {
opCode: '==',
leftSide: '',
rightSide: ''
}
condition.rules.splice(index + 1, 0, rule)
}
const addConditionGroup = (conditions) => {
const condition = {
and: true,
rules: [
{
opCode: '==',
leftSide: '',
rightSide: ''
}
]
}
conditions.push(condition)
}
/** 保存条件设置 */
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) {
message.warning('请完善条件规则')
return
}
dialogVisible.value = false
//
emit('updateCondition', condition.value)
}
const open = (conditionObj: any | undefined) => {
if (conditionObj) {
condition.value.conditionType = conditionObj.conditionType
condition.value.conditionExpression = conditionObj.conditionExpression
condition.value.conditionGroups = conditionObj.conditionGroups
}
dialogVisible.value = true
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.condition-group-tool {
display: flex;
justify-content: space-between;
width: 500px;
margin-bottom: 20px;
}
.condition-group {
position: relative;
&:hover {
border-color: #0089ff;
.condition-group-delete {
opacity: 1;
}
}
.condition-group-delete {
position: absolute;
top: 0;
left: 0;
display: flex;
cursor: pointer;
opacity: 0;
}
}
::v-deep(.el-card__header) {
padding: 8px var(--el-card-padding);
border-bottom: 1px solid var(--el-card-border-color);
box-sizing: border-box;
}
</style>

View File

@ -9,7 +9,14 @@
]"
>
<div class="node-title-container">
<div class="node-title-icon user-task"><span class="iconfont icon-approve"></span></div>
<div
:class="`node-title-icon ${currentNode.type === NodeType.TRANSACTOR_NODE ? 'transactor-task' : 'user-task'}`"
>
<span
:class="`iconfont ${currentNode.type === NodeType.TRANSACTOR_NODE ? 'icon-transactor' : 'icon-approve'}`"
>
</span>
</div>
<input
v-if="!readonly && showInput"
type="text"
@ -28,7 +35,7 @@
{{ currentNode.showText }}
</div>
<div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(NodeType.USER_TASK_NODE) }}
{{ NODE_DEFAULT_TEXT.get(currentNode.type) }}
</div>
<Icon icon="ep:arrow-right-bold" v-if="!readonly" />
</div>

View File

@ -177,6 +177,10 @@
color: #ca3a31
}
.transactor {
color: #330099;
}
.handler-item-text {
margin-top: 4px;
width: 80px;
@ -290,10 +294,14 @@
&.trigger-node {
color: #3373d2;
}
&.router-node {
color: #ca3a31
}
&.transactor-task {
color: #330099;
}
}
.node-title {
@ -777,7 +785,7 @@
content: "\e7eb";
}
.icon-handle:before {
.icon-transactor:before {
content: "\e61c";
}

View File

@ -191,6 +191,7 @@ import {
} from '@/components/SimpleProcessDesignerV2/src/consts'
import * as UserApi from '@/api/system/user'
import { useFormFieldsPermission } from '@/components/SimpleProcessDesignerV2/src/node'
import { BpmModelFormType } from '@/utils/constants'
defineOptions({ name: 'ElementCustomConfig4UserTask' })
const props = defineProps({
@ -248,7 +249,6 @@ const resetCustomConfigList = () => {
bpmnElement.value.id,
bpmnInstances().modeler
)
//
elExtensionElements.value =
bpmnElement.value.businessObject?.extensionElements ??
@ -311,14 +311,13 @@ const resetCustomConfigList = () => {
}
//
if (formType.value === 10) {
if (formType.value === BpmModelFormType.NORMAL) {
const fieldsPermissionList = elExtensionElements.value.values?.filter(
(ex) => ex.$type === `${prefix}:FieldsPermission`
)
fieldsPermissionEl.value = []
getNodeConfigFormFields()
//
fieldsPermissionConfig.value = fieldsPermissionConfig.value.slice(1)
fieldsPermissionConfig.value = fieldsPermissionConfig.value
fieldsPermissionConfig.value.forEach((element) => {
element.permission =
fieldsPermissionList?.find((obj) => obj.field === element.field)?.permission ?? '1'
@ -497,9 +496,9 @@ onMounted(async () => {
<style lang="scss" scoped>
.button-setting-pane {
display: flex;
flex-direction: column;
font-size: 14px;
margin-top: 8px;
font-size: 14px;
flex-direction: column;
.button-setting-desc {
padding-right: 8px;

View File

@ -453,14 +453,14 @@ export function jsonParse(str: string) {
/**
*
*
* @param name
* @param start
* @param end
* @param str
* @param start
* @param end
*/
export const sliceName = (name: string,start: number, end : number) => {
if (name.length > end) {
return name.slice(start, end)
export const subString = (str: string, start: number, end: number) => {
if (str.length > end) {
return str.slice(start, end)
}
return name
return str
}

View File

@ -89,7 +89,7 @@
</el-tooltip>
<el-image v-if="row.icon" :src="row.icon" class="h-38px w-38px mr-10px rounded" />
<div v-else class="flow-icon">
<span style="font-size: 12px; color: #fff">{{ sliceName(row.name,0,2) }}</span>
<span style="font-size: 12px; color: #fff">{{ subString(row.name, 0, 2) }}</span>
</div>
{{ row.name }}
</div>
@ -273,7 +273,7 @@ import { useAppStore } from '@/store/modules/app'
import { cloneDeep, isEqual } from 'lodash-es'
import { useTagsView } from '@/hooks/web/useTagsView'
import { useDebounceFn } from '@vueuse/core'
import { sliceName } from '@/utils/index'
import { subString } from '@/utils/index'
defineOptions({ name: 'BpmModel' })

View File

@ -34,6 +34,8 @@ import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/d
//
import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
import * as ModelApi from '@/api/bpm/model'
import { BpmModelFormType } from '@/utils/constants'
import * as FormApi from '@/api/bpm/form'
defineOptions({ name: 'BpmModelEditor' })
@ -49,7 +51,8 @@ const message = useMessage() // 国际化
//
const formFields = ref<string[]>([])
const formType = ref(20)
//
const formType = ref(BpmModelFormType.NORMAL)
provide('formFields', formFields)
provide('formType', formType)
@ -88,6 +91,20 @@ const save = async (bpmnXml: string) => {
}
}
/** 监听表单 ID 变化,加载表单数据 */
watch(
() => modelData.value.formId,
async (newFormId) => {
if (newFormId && modelData.value.formType === BpmModelFormType.NORMAL) {
const data = await FormApi.getForm(newFormId)
formFields.value = data.fields
} else {
formFields.value = []
}
},
{ immediate: true }
)
//
onBeforeUnmount(() => {
modeler.value = null

View File

@ -11,12 +11,12 @@
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="modelData.formType === 10" label="流程表单" prop="formId">
<el-form-item v-if="modelData.formType === BpmModelFormType.NORMAL" label="流程表单" prop="formId">
<el-select v-model="modelData.formId" clearable style="width: 100%">
<el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
</el-select>
</el-form-item>
<el-form-item v-if="modelData.formType === 20" label="表单提交路由" prop="formCustomCreatePath">
<el-form-item v-if="modelData.formType === BpmModelFormType.CUSTOM" label="表单提交路由" prop="formCustomCreatePath">
<el-input
v-model="modelData.formCustomCreatePath"
placeholder="请输入表单提交路由"
@ -31,7 +31,7 @@
<Icon icon="ep:question" class="ml-5px" />
</el-tooltip>
</el-form-item>
<el-form-item v-if="modelData.formType === 20" label="表单查看地址" prop="formCustomViewPath">
<el-form-item v-if="modelData.formType === BpmModelFormType.CUSTOM" label="表单查看地址" prop="formCustomViewPath">
<el-input
v-model="modelData.formCustomViewPath"
placeholder="请输入表单查看的组件地址"
@ -48,7 +48,7 @@
</el-form-item>
<!-- 表单预览 -->
<div
v-if="modelData.formType === 10 && modelData.formId && formPreview.rule.length > 0"
v-if="modelData.formType === BpmModelFormType.NORMAL && modelData.formId && formPreview.rule.length > 0"
class="mt-20px"
>
<div class="flex items-center mb-15px">
@ -68,6 +68,7 @@
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as FormApi from '@/api/bpm/form'
import { setConfAndFields2 } from '@/utils/formCreate'
import { BpmModelFormType } from '@/utils/constants'
const props = defineProps({
formList: {
@ -96,7 +97,7 @@ const formPreview = ref({
watch(
() => modelData.value.formId,
async (newFormId) => {
if (newFormId && modelData.value.formType === 10) {
if (newFormId && modelData.value.formType === BpmModelFormType.NORMAL) {
const data = await FormApi.getForm(newFormId)
setConfAndFields2(formPreview.value, data.conf, data.fields)
//

View File

@ -64,9 +64,9 @@
class="w-32px h-32px"
/>
<div v-else class="flow-icon">
<span style="font-size: 12px; color: #fff">{{
sliceName(definition.name,0,2)
}}</span>
<span style="font-size: 12px; color: #fff">
{{ subString(definition.name, 0, 2) }}
</span>
</div>
<el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
</div>
@ -97,7 +97,7 @@ import * as ProcessInstanceApi from '@/api/bpm/processInstance'
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
import ProcessDefinitionDetail from './ProcessDefinitionDetail.vue'
import { groupBy } from 'lodash-es'
import { sliceName } from '@/utils/index'
import { subString } from '@/utils/index'
defineOptions({ name: 'BpmProcessInstanceCreate' })

View File

@ -36,10 +36,10 @@
:rule="approveForm.rule"
/>
</el-card>
<el-form-item label="审批意见" prop="reason">
<el-form-item :label="`${nodeTypeName}意见`" prop="reason">
<el-input
v-model="approveReasonForm.reason"
placeholder="请输入审批意见"
:placeholder="`请输入${nodeTypeName}意见`"
type="textarea"
:rows="4"
/>
@ -504,6 +504,7 @@ import * as TaskApi from '@/api/bpm/task'
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
import * as UserApi from '@/api/system/user'
import {
NodeType,
OPERATION_BUTTON_NAME,
OperationButtonType
} from '@/components/SimpleProcessDesignerV2/src/consts'
@ -546,6 +547,7 @@ const returnList = ref([] as any) // 退回节点
const runningTask = ref<any>() //
const approveForm = ref<any>({}) //
const approveFormFApi = ref<any>({}) // approveForms fAPi
const nodeTypeName = ref('审批') //
//
const reasonRequire = ref()
@ -558,7 +560,9 @@ const approveReasonForm = reactive({
})
const approveReasonRule = computed(() => {
return {
reason: [{ required: reasonRequire.value, message: '审批意见不能为空', trigger: 'blur' }],
reason: [
{ required: reasonRequire.value, message: nodeTypeName + '意见不能为空', trigger: 'blur' }
],
signPicUrl: [{ required: true, message: '签名不能为空', trigger: 'change' }]
}
})
@ -969,9 +973,10 @@ const getButtonDisplayName = (btnType: OperationButtonType) => {
const loadTodoTask = (task: any) => {
approveForm.value = {}
approveFormFApi.value = {}
runningTask.value = task
approveFormFApi.value = {}
reasonRequire.value = task?.reasonRequire ?? false
nodeTypeName.value = task?.nodeType === NodeType.TRANSACTOR_NODE ? '办理' : '审批'
// approve .
if (task && task.formId && task.formConf) {
const tempApproveForm = {}

View File

@ -84,7 +84,8 @@ const setSimpleModelNodeTaskStatus = (
//
if (
simpleModel.type === NodeType.START_USER_NODE ||
simpleModel.type === NodeType.USER_TASK_NODE
simpleModel.type === NodeType.USER_TASK_NODE ||
simpleModel.type === NodeType.TRANSACTOR_NODE
) {
simpleModel.activityStatus = TaskStatusEnum.NOT_START
if (rejectedTaskActivityIds.includes(simpleModel.id)) {

View File

@ -121,6 +121,7 @@
"
class="text-#a5a5a5 text-13px mt-1 w-full bg-#f8f8fa p2 rounded-md"
>
<!-- TODO lesan这里如果是办理需要是办理意见 -->
审批意见{{ task.reason }}
</div>
<div
@ -179,6 +180,7 @@ import copySvg from '@/assets/svgs/bpm/copy.svg'
import conditionSvg from '@/assets/svgs/bpm/condition.svg'
import parallelSvg from '@/assets/svgs/bpm/parallel.svg'
import finishSvg from '@/assets/svgs/bpm/finish.svg'
import transactorSvg from '@/assets/svgs/bpm/transactor.svg'
defineOptions({ name: 'BpmProcessInstanceTimeline' })
withDefaults(
@ -240,6 +242,8 @@ const nodeTypeSvgMap = {
[NodeType.START_USER_NODE]: { color: '#909398', svg: starterSvg },
//
[NodeType.USER_TASK_NODE]: { color: '#ff943e', svg: auditorSvg },
//
[NodeType.TRANSACTOR_NODE]: { color: '#ff943e', svg: transactorSvg },
//
[NodeType.COPY_TASK_NODE]: { color: '#3296fb', svg: copySvg },
//
@ -264,6 +268,7 @@ const getApprovalNodeIcon = (taskStatus: number, nodeType: NodeType) => {
if (
nodeType === NodeType.START_USER_NODE ||
nodeType === NodeType.USER_TASK_NODE ||
nodeType === NodeType.TRANSACTOR_NODE ||
nodeType === NodeType.END_EVENT_NODE
) {
return statusIconMap[taskStatus]?.icon