# Conflicts:
#	src/views/system/menu/index.vue
This commit is contained in:
YunaiV
2025-02-09 07:39:48 +08:00
17 changed files with 645 additions and 102 deletions

View File

@@ -246,15 +246,15 @@ export type AssignEmptyHandler = {
export type ListenerHandler = {
enable: boolean
path?: string
header?: ListenerParam[]
body?: ListenerParam[]
header?: HttpRequestParam[]
body?: HttpRequestParam[]
}
export type ListenerParam = {
export type HttpRequestParam = {
key: string
type: number
value: string
}
export enum ListenerParamTypeEnum {
export enum BpmHttpRequestParamTypeEnum {
/**
* 固定值
*/
@@ -264,7 +264,7 @@ export enum ListenerParamTypeEnum {
*/
FROM_FORM = 2
}
export const LISTENER_MAP_TYPES = [
export const BPM_HTTP_REQUEST_PARAM_TYPES = [
{
value: 1,
label: '固定值'
@@ -371,13 +371,13 @@ export enum TimeUnitType {
/**
* 条件节点设置结构定义,用于条件节点
*/
export type ConditionSetting = {
export type ConditionSetting = {
// 条件类型
conditionType?: ConditionType,
conditionType?: ConditionType
// 条件表达式
conditionExpression?: string,
conditionExpression?: string
// 条件组
conditionGroups?: ConditionGroup,
conditionGroups?: ConditionGroup
// 是否默认的条件
defaultFlow?: boolean
}
@@ -710,13 +710,14 @@ export type RouterSetting = {
conditionGroups: ConditionGroup
}
// ==================== 触发器相关定义 ====================
// ==================== 触发器相关定义 ====================
/**
* 触发器节点结构定义
*/
export type TriggerSetting = {
type: TriggerTypeEnum
httpRequestSetting: HttpRequestTriggerSetting
httpRequestSetting?: HttpRequestTriggerSetting
normalFormSetting?: NormalFormTriggerSetting
}
/**
@@ -727,6 +728,10 @@ export enum TriggerTypeEnum {
* 发送 HTTP 请求触发器
*/
HTTP_REQUEST = 1,
/**
* 更新流程表单触发器
*/
UPDATE_NORMAL_FORM = 2 // TODO @jasonFORM_UPDATE
}
/**
@@ -736,11 +741,22 @@ export type HttpRequestTriggerSetting = {
// 请求 URL
url: string
// 请求头参数设置
header?: ListenerParam[] // TODO 需要重命名一下
header?: HttpRequestParam[]
// 请求体参数设置
body?: ListenerParam[]
body?: HttpRequestParam[]
// 请求响应设置
response?: Record<string, string>[]
}
/**
* 流程表单触发器配置结构定义
*/
export type NormalFormTriggerSetting = {
// 更新表单字段
updateFormFields?: Record<string, any>
}
export const TRIGGER_TYPES: DictDataVO[] = [
{ label: 'HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST }
{ label: 'HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST },
{ label: '修改表单数据', value: TriggerTypeEnum.UPDATE_NORMAL_FORM }
]

View File

@@ -14,7 +14,8 @@ import {
AssignStartUserHandlerType,
AssignEmptyHandlerType,
FieldPermissionType,
ListenerParam
HttpRequestParam,
ProcessVariableEnum
} from './consts'
import { parseFormFields } from '@/components/FormCreate/src/utils'
@@ -105,14 +106,31 @@ export function useFormFieldsPermission(defaultPermission: FieldPermissionType)
getNodeConfigFormFields
}
}
/**
* @description 获取表单的字段
* @description 获取流程表单的字段
*/
export function useFormFields() {
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
return parseFormCreateFields(unref(formFields))
}
// TODO @芋艿:后续需要把各种类似 useFormFieldsPermission 的逻辑,抽成一个通用方法。
/**
* @description 获取流程表单的字段和发起人字段
*/
export function useFormFieldsAndStartUser() {
const injectFormFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
const formFields = parseFormCreateFields(unref(injectFormFields))
// 添加发起人
formFields.unshift({
field: ProcessVariableEnum.START_USER_ID,
title: '发起人',
required: true
})
return formFields
}
export type UserTaskFormType = {
candidateStrategy: CandidateStrategy
approveMethod: ApproveMethodType
@@ -139,20 +157,20 @@ export type UserTaskFormType = {
taskCreateListenerEnable?: boolean
taskCreateListenerPath?: string
taskCreateListener?: {
header: ListenerParam[],
body: ListenerParam[]
header: HttpRequestParam[]
body: HttpRequestParam[]
}
taskAssignListenerEnable?: boolean
taskAssignListenerPath?: string
taskAssignListener?: {
header: ListenerParam[],
body: ListenerParam[]
header: HttpRequestParam[]
body: HttpRequestParam[]
}
taskCompleteListenerEnable?: boolean
taskCompleteListenerPath?: string
taskCompleteListener?:{
header: ListenerParam[],
body: ListenerParam[]
taskCompleteListener?: {
header: HttpRequestParam[]
body: HttpRequestParam[]
}
signEnable: boolean
reasonRequire: boolean

View File

@@ -47,10 +47,9 @@ import {
SimpleFlowNode,
ConditionType,
COMPARISON_OPERATORS,
ProcessVariableEnum
} from '../consts'
import { getDefaultConditionNodeName } from '../utils'
import { useFormFields } from '../node'
import { useFormFieldsAndStartUser } from '../node'
import Condition from './components/Condition.vue'
const message = useMessage() // 消息弹窗
defineOptions({
@@ -176,23 +175,12 @@ const getShowText = (): string => {
}
return showText
}
const fieldsInfo = useFormFields()
/** 条件规则可选择的表单字段 */
const fieldOptions = computed(() => {
const fieldsCopy = fieldsInfo.slice()
// 固定添加发起人 ID 字段
fieldsCopy.unshift({
field: ProcessVariableEnum.START_USER_ID,
title: '发起人',
required: true
})
return fieldsCopy
})
// 流程表单字段和发起人字段
const fieldOptions = useFormFieldsAndStartUser()
/** 获取字段名称 */
const getFieldTitle = (field: string) => {
const item = fieldOptions.value.find((item) => item.field === field)
const item = fieldOptions.find((item) => item.field === field)
return item?.title
}

View File

@@ -124,6 +124,7 @@ const saveConfig = async () => {
if (!valid) return false
const showText = getShowText()
if (!showText) return false
currentNode.value.name = nodeName.value!
currentNode.value.showText = showText
if (configForm.value.delayType === DelayTypeEnum.FIXED_TIME_DURATION) {
currentNode.value.delaySetting = {

View File

@@ -35,6 +35,7 @@
/>
</el-select>
</el-form-item>
<!-- HTTP 请求触发器 -->
<div
v-if="configForm.type === TriggerTypeEnum.HTTP_REQUEST && configForm.httpRequestSetting"
>
@@ -46,14 +47,137 @@
:closable="false"
/>
</el-form-item>
<!-- 请求地址-->
<el-form-item label="请求地址" prop="httpRequestSetting.url">
<el-input v-model="configForm.httpRequestSetting.url" />
</el-form-item>
<!-- 请求头请求体设置-->
<HttpRequestParamSetting
:header="configForm.httpRequestSetting.header"
:body="configForm.httpRequestSetting.body"
:bind="'httpRequestSetting'"
/>
<!-- 返回值设置-->
<el-form-item label="返回值">
<el-alert
title="通过请求返回值, 可以修改流程表单的值"
type="warning"
show-icon
:closable="false"
/>
</el-form-item>
<el-form-item>
<div
class="flex pt-2"
v-for="(item, index) in configForm.httpRequestSetting.response"
:key="index"
>
<div class="mr-2">
<el-form-item
:prop="`httpRequestSetting.response.${index}.key`"
:rules="{
required: true,
message: '表单字段不能为空',
trigger: 'blur'
}"
>
<el-select class="w-160px!" v-model="item.key" placeholder="请选择表单字段">
<el-option
v-for="(field, fIdx) in formFields"
:key="fIdx"
:label="field.title"
:value="field.field"
:disabled="!field.required"
/>
</el-select>
</el-form-item>
</div>
<div class="mr-2">
<el-form-item
:prop="`httpRequestSetting.response.${index}.value`"
:rules="{
required: true,
message: '请求返回字段不能为空',
trigger: 'blur'
}"
>
<el-input class="w-160px" v-model="item.value" placeholder="请求返回字段" />
</el-form-item>
</div>
<div class="mr-1 pt-1 cursor-pointer">
<Icon
icon="ep:delete"
:size="18"
@click="deleteHttpResponseSetting(configForm.httpRequestSetting.response!, index)"
/>
</div>
</div>
<el-button
type="primary"
text
@click="addHttpResponseSetting(configForm.httpRequestSetting.response!)"
>
<Icon icon="ep:plus" class="mr-5px" />添加一行
</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 !== ''"
>
<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'
}"
>
<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>
</div>
<el-button type="primary" text @click="addFormFieldSetting()">
<Icon icon="ep:plus" class="mr-5px" />添加修改字段
</el-button>
</div>
</el-form>
</div>
@@ -68,7 +192,7 @@
</template>
<script setup lang="ts">
import { SimpleFlowNode, NodeType, TriggerSetting, TRIGGER_TYPES, TriggerTypeEnum } from '../consts'
import { useWatchNode, useDrawer, useNodeName } from '../node'
import { useWatchNode, useDrawer, useNodeName, useFormFields } from '../node'
import HttpRequestParamSetting from './components/HttpRequestParamSetting.vue'
defineOptions({
@@ -80,6 +204,7 @@ const props = defineProps({
required: true
}
})
const message = useMessage() // 消息弹窗
// 抽屉配置
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
// 当前节点
@@ -91,9 +216,7 @@ const formRef = ref() // 表单 Ref
// 表单校验规则
const formRules = reactive({
type: [{ required: true, message: '触发器类型不能为空', trigger: 'change' }],
httpRequestSetting: {
url: [{ required: true, message: '请求地址不能为空', trigger: 'blur' }]
}
'httpRequestSetting.url': [{ required: true, message: '请求地址不能为空', trigger: 'blur' }]
})
// 触发器配置表单数据
const configForm = ref<TriggerSetting>({
@@ -101,9 +224,56 @@ const configForm = ref<TriggerSetting>({
httpRequestSetting: {
url: '',
header: [],
body: []
}
body: [],
response: []
},
normalFormSetting: { 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)
}))
})
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({
key: '',
value: ''
})
}
/** 删除 HTTP 请求返回值设置项 */
const deleteHttpResponseSetting = (responseSetting: Record<string, string>[], index: number) => {
responseSetting.splice(index, 1)
}
/** 添加修改表单设置项 */
const addFormFieldSetting = () => {
if (configForm.value.normalFormSetting!.updateFormFields === undefined) {
configForm.value.normalFormSetting!.updateFormFields = {}
}
configForm.value.normalFormSetting!.updateFormFields[''] = undefined
}
/** 删除修改表单设置项 */
const deleteFormFieldSetting = (key: string) => {
if (!configForm.value.normalFormSetting?.updateFormFields) return
delete configForm.value.normalFormSetting.updateFormFields[key]
}
/** 保存配置 */
const saveConfig = async () => {
@@ -112,16 +282,31 @@ const saveConfig = async () => {
if (!valid) return false
const showText = getShowText()
if (!showText) return false
currentNode.value.name = nodeName.value!
currentNode.value.showText = showText
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
configForm.value.normalFormSetting = undefined
}
if (configForm.value.type === TriggerTypeEnum.UPDATE_NORMAL_FORM) {
configForm.value.httpRequestSetting = undefined
}
currentNode.value.triggerSetting = configForm.value
settingVisible.value = false
return true
}
/** 获取节点展示内容 */
const getShowText = (): string => {
let showText = ''
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
showText = `${configForm.value.httpRequestSetting.url}`
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 = '修改表单数据'
}
}
return showText
}
@@ -130,8 +315,16 @@ const getShowText = (): string => {
const showTriggerNodeConfig = (node: SimpleFlowNode) => {
nodeName.value = node.name
if (node.triggerSetting) {
configForm.value.type = node.triggerSetting.type
configForm.value.httpRequestSetting = node.triggerSetting.httpRequestSetting
configForm.value = {
type: node.triggerSetting.type,
httpRequestSetting: node.triggerSetting.httpRequestSetting || {
url: '',
header: [],
body: [],
response: []
},
normalFormSetting: node.triggerSetting.normalFormSetting || { updateFormFields: {} }
}
}
}

View File

@@ -61,7 +61,7 @@
label="指定角色"
prop="roleIds"
>
<el-select 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 +99,7 @@
prop="postIds"
span="24"
>
<el-select 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 +114,7 @@
prop="userIds"
span="24"
>
<el-select 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 +128,7 @@
label="指定用户组"
prop="userGroups"
>
<el-select 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"
@@ -142,7 +142,7 @@
label="表单内用户字段"
prop="formUser"
>
<el-select v-model="configForm.formUser" clearable style="width: 100%">
<el-select filterable v-model="configForm.formUser" clearable style="width: 100%">
<el-option
v-for="(item, idx) in userFieldOnFormOptions"
:key="idx"
@@ -157,7 +157,7 @@
label="表单内部门字段"
prop="formDept"
>
<el-select v-model="configForm.formDept" clearable style="width: 100%">
<el-select filterable v-model="configForm.formDept" clearable style="width: 100%">
<el-option
v-for="(item, idx) in deptFieldOnFormOptions"
:key="idx"
@@ -179,7 +179,7 @@
prop="deptLevel"
span="24"
>
<el-select v-model="configForm.deptLevel" clearable>
<el-select filterable v-model="configForm.deptLevel" clearable>
<el-option
v-for="(item, index) in MULTI_LEVEL_DEPT"
:key="index"
@@ -245,7 +245,7 @@
label="驳回节点"
prop="returnNodeId"
>
<el-select v-model="configForm.returnNodeId" clearable style="width: 100%">
<el-select filterable v-model="configForm.returnNodeId" clearable style="width: 100%">
<el-option
v-for="item in returnTaskList"
:key="item.id"
@@ -293,6 +293,7 @@
/>
</el-form-item>
<el-select
filterable
v-model="timeUnit"
class="mr-2"
:style="{ width: '100px' }"
@@ -332,6 +333,7 @@
span="24"
>
<el-select
filterable
v-model="configForm.assignEmptyHandlerUserIds"
clearable
multiple
@@ -758,22 +760,22 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
getNodeConfigFormFields(node.fieldsPermission)
// 5. 监听器
// 5.1 创建任务
configForm.value.taskCreateListenerEnable = node.taskCreateListener!.enable
configForm.value.taskCreateListenerPath = node.taskCreateListener!.path
configForm.value.taskCreateListenerEnable = node.taskCreateListener?.enable
configForm.value.taskCreateListenerPath = node.taskCreateListener?.path
configForm.value.taskCreateListener = {
header: node.taskCreateListener?.header ?? [],
body: node.taskCreateListener?.body ?? []
}
// 5.2 指派任务
configForm.value.taskAssignListenerEnable = node.taskAssignListener!.enable
configForm.value.taskAssignListenerPath = node.taskAssignListener!.path
configForm.value.taskAssignListenerEnable = node.taskAssignListener?.enable
configForm.value.taskAssignListenerPath = node.taskAssignListener?.path
configForm.value.taskAssignListener = {
header: node.taskAssignListener?.header ?? [],
body: node.taskAssignListener?.body ?? []
}
// 5.3 完成任务
configForm.value.taskCompleteListenerEnable = node.taskCompleteListener!.enable
configForm.value.taskCompleteListenerPath = node.taskCompleteListener!.path
configForm.value.taskCompleteListenerEnable = node.taskCompleteListener?.enable
configForm.value.taskCompleteListenerPath = node.taskCompleteListener?.path
configForm.value.taskCompleteListener = {
header: node.taskCompleteListener?.header ?? [],
body: node.taskCompleteListener?.body ?? []

View File

@@ -138,11 +138,10 @@ import {
COMPARISON_OPERATORS,
CONDITION_CONFIG_TYPES,
ConditionType,
DEFAULT_CONDITION_GROUP_VALUE,
ProcessVariableEnum
DEFAULT_CONDITION_GROUP_VALUE
} from '../../consts'
import { BpmModelFormType } from '@/utils/constants'
import { useFormFields } from '../../node'
import { useFormFieldsAndStartUser } from '../../node'
const props = defineProps({
modelValue: {
@@ -170,17 +169,10 @@ const conditionConfigTypes = computed(() => {
}
})
})
/** 条件规则可选择的表单字段 */
const fieldOptions = computed(() => {
const fieldsCopy = useFormFields().slice()
// 固定添加发起人 ID 字段
fieldsCopy.unshift({
field: ProcessVariableEnum.START_USER_ID,
title: '发起人',
required: true
})
return fieldsCopy
})
const fieldOptions = useFormFieldsAndStartUser()
// 表单校验规则
const formRules = reactive({
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],

View File

@@ -16,7 +16,7 @@
<div class="mr-2">
<el-select class="w-100px!" v-model="item.type">
<el-option
v-for="types in LISTENER_MAP_TYPES"
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
:key="types.value"
:label="types.label"
:value="types.value"
@@ -33,7 +33,7 @@
}"
>
<el-input
v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
class="w-160px"
v-model="item.value"
/>
@@ -47,7 +47,7 @@
}"
>
<el-select
v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
class="w-160px!"
v-model="item.value"
>
@@ -86,7 +86,7 @@
<div class="mr-2">
<el-select class="w-100px!" v-model="item.type">
<el-option
v-for="types in LISTENER_MAP_TYPES"
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
:key="types.value"
:label="types.label"
:value="types.value"
@@ -103,7 +103,7 @@
}"
>
<el-input
v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
class="w-160px"
v-model="item.value"
/>
@@ -117,7 +117,7 @@
}"
>
<el-select
v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
class="w-160px!"
v-model="item.value"
>
@@ -141,20 +141,20 @@
</el-form-item>
</template>
<script setup lang="ts">
import { ListenerParam, LISTENER_MAP_TYPES, ListenerParamTypeEnum } from '../../consts'
import { useFormFields } from '../../node'
import { HttpRequestParam, BPM_HTTP_REQUEST_PARAM_TYPES, BpmHttpRequestParamTypeEnum } from '../../consts'
import { useFormFieldsAndStartUser } from '../../node'
defineOptions({
name: 'HttpRequestParamSetting'
})
const props = defineProps({
header: {
type: Array as () => ListenerParam[],
type: Array as () => HttpRequestParam[],
required: false,
default: () => []
},
body: {
type: Array as () => ListenerParam[],
type: Array as () => HttpRequestParam[],
required: false,
default: () => []
},
@@ -164,16 +164,19 @@ const props = defineProps({
}
})
const formFieldOptions = useFormFields()
const addHttpRequestParam = (arr: ListenerParam[]) => {
// 流程表单字段,发起人字段
const formFieldOptions = useFormFieldsAndStartUser()
/** 添加请求配置项 */
const addHttpRequestParam = (arr: HttpRequestParam[]) => {
arr.push({
key: '',
type: ListenerParamTypeEnum.FIXED_VALUE,
type: BpmHttpRequestParamTypeEnum.FIXED_VALUE,
value: ''
})
}
const deleteHttpRequestParam = (arr: ListenerParam[], index: number) => {
/** 删除请求配置项 */
const deleteHttpRequestParam = (arr: HttpRequestParam[], index: number) => {
arr.splice(index, 1)
}
</script>