diff --git a/src/components/FormCreate/src/components/useApiSelect.tsx b/src/components/FormCreate/src/components/useApiSelect.tsx index 29cd3027..741b1d48 100644 --- a/src/components/FormCreate/src/components/useApiSelect.tsx +++ b/src/components/FormCreate/src/components/useApiSelect.tsx @@ -185,7 +185,7 @@ export const useApiSelect = (option: ApiSelectProps) => { ) } - debugger + // debugger return ( > + fieldsPermission?: Array> // 审批任务超时处理 timeoutHandler?: TimeoutHandler // 审批任务拒绝处理 @@ -145,6 +145,14 @@ export enum CandidateStrategy { * 指定用户组 */ USER_GROUP = 40, + /** + * 表单内用户字段 + */ + USER_FIELD_ON_FORM = 50, + /** + * 表单内部门负责人 + */ + DEPT_LEADER_ON_FORM = 51, /** * 流程表达式 */ @@ -424,6 +432,8 @@ export const CANDIDATE_STRATEGY: DictDataVO[] = [ { label: '发起人部门负责人', value: CandidateStrategy.START_USER_DEPT_LEADER }, { label: '发起人连续部门负责人', value: CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER }, { label: '用户组', value: CandidateStrategy.USER_GROUP }, + { label: '表单内用户字段', value: CandidateStrategy.USER_FIELD_ON_FORM }, + { label: '表单内部门负责人', value: CandidateStrategy.DEPT_LEADER_ON_FORM }, { label: '流程表达式', value: CandidateStrategy.EXPRESSION } ] // 审批节点 的审批类型 @@ -548,3 +558,13 @@ export const MULTI_LEVEL_DEPT: DictDataVO = [ { label: '第 14 级部门', value: 14 }, { label: '第 15 级部门', value: 15 } ] + +/** + * 流程实例的变量枚举 + */ +export enum ProcessVariableEnum { + /** + * 发起用户 ID + */ + START_USER_ID = 'PROCESS_START_USER_ID' +} diff --git a/src/components/SimpleProcessDesignerV2/src/node.ts b/src/components/SimpleProcessDesignerV2/src/node.ts index d10173bb..167e2c5d 100644 --- a/src/components/SimpleProcessDesignerV2/src/node.ts +++ b/src/components/SimpleProcessDesignerV2/src/node.ts @@ -14,7 +14,8 @@ import { NODE_DEFAULT_NAME, AssignStartUserHandlerType, AssignEmptyHandlerType, - FieldPermissionType + FieldPermissionType, + ProcessVariableEnum } from './consts' export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref { const node = ref(props.flowNode) @@ -27,12 +28,67 @@ export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref { + const result: Array> = [] + if (formFields) { + formFields.forEach((fieldStr: string) => { + parseFields(JSON.parse(fieldStr), result) + }) + } + // 固定添加发起人 ID 字段 + result.unshift( { + field: ProcessVariableEnum.START_USER_ID, + title: '发起人', + type: 'UserSelect', + required: true + }) + return result +} + +const parseFields = ( + rule: Record, + fields: Array>, + parentTitle: string = '' +) => { + const { type, field, $required, title: tempTitle, children } = rule + if (field && tempTitle) { + let title = tempTitle + if (parentTitle) { + title = `${parentTitle}.${tempTitle}` + } + let required = false; + if($required) { + required = true; + } + fields.push({ + field, + title, + type, + required + }) + // TODO 子表单 需要处理子表单字段 + // if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) { + // // 解析子表单的字段 + // rule.props.rule.forEach((item) => { + // parseFields(item, fieldsPermission, title) + // }) + // } + } + if (children && Array.isArray(children)) { + children.forEach((rule) => { + parseFields(rule, fields) + }) + } +} + + /** * @description 表单数据权限配置,用于发起人节点 、审批节点、抄送节点 */ export function useFormFieldsPermission(defaultPermission: FieldPermissionType) { // 字段权限配置. 需要有 field, title, permissioin 属性 - const fieldsPermissionConfig = ref>>([]) + const fieldsPermissionConfig = ref>>([]) const formType = inject>('formType') // 表单类型 @@ -45,49 +101,28 @@ export function useFormFieldsPermission(defaultPermission: FieldPermissionType) } // 默认的表单权限: 获取表单的所有字段,设置字段默认权限为只读 const getDefaultFieldsPermission = (formFields?: string[]) => { - const defaultFieldsPermission: Array> = [] + let defaultFieldsPermission: Array> = [] if (formFields) { - formFields.forEach((fieldStr: string) => { - parseFieldsSetDefaultPermission(JSON.parse(fieldStr), defaultFieldsPermission) - }) + defaultFieldsPermission = parseFormCreateFields(formFields).map( item => { + return { + field: item.field, + title: item.title, + permission: defaultPermission + } + }); } return defaultFieldsPermission } - // 解析字段。赋给默认权限 - const parseFieldsSetDefaultPermission = ( - rule: Record, - fieldsPermission: Array>, - parentTitle: string = '' - ) => { - const { /**type,*/ field, title: tempTitle, children } = rule - if (field && tempTitle) { - let title = tempTitle - if (parentTitle) { - title = `${parentTitle}.${tempTitle}` - } - fieldsPermission.push({ - field, - title, - permission: defaultPermission - }) - // TODO 子表单 需要处理子表单字段 - // if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) { - // // 解析子表单的字段 - // rule.props.rule.forEach((item) => { - // parseFieldsSetDefaultPermission(item, fieldsPermission, title) - // }) - // } - } - if (children && Array.isArray(children)) { - children.forEach((rule) => { - parseFieldsSetDefaultPermission(rule, fieldsPermission) - }) - } - } + + + + // 获取表单的所有字段,作为下拉框选项 + const formFieldOptions = parseFormCreateFields(unref(formFields)) return { formType, fieldsPermissionConfig, + formFieldOptions, getNodeConfigFormFields } } @@ -95,50 +130,8 @@ export function useFormFieldsPermission(defaultPermission: FieldPermissionType) * @description 获取表单的字段 */ export function useFormFields() { - // 解析后的表单字段 const formFields = inject>('formFields') // 流程表单字段 - const parseFormFields = () => { - const parsedFormFields: Array> = [] - if (formFields) { - formFields.value.forEach((fieldStr: string) => { - parseField(JSON.parse(fieldStr), parsedFormFields) - }) - } - return parsedFormFields - } - // 解析字段。 - const parseField = ( - rule: Record, - parsedFormFields: Array>, - parentTitle: string = '' - ) => { - const { field, title: tempTitle, children, type } = rule - if (field && tempTitle) { - let title = tempTitle - if (parentTitle) { - title = `${parentTitle}.${tempTitle}` - } - parsedFormFields.push({ - field, - title, - type - }) - // TODO 子表单 需要处理子表单字段 - // if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) { - // // 解析子表单的字段 - // rule.props.rule.forEach((item) => { - // parseFieldsSetDefaultPermission(item, fieldsPermission, title) - // }) - // } - } - if (children && Array.isArray(children)) { - children.forEach((rule) => { - parseField(rule, parsedFormFields) - }) - } - } - - return parseFormFields() + return parseFormCreateFields(unref(formFields)) } export type UserTaskFormType = { @@ -152,6 +145,8 @@ export type UserTaskFormType = { userGroups?: number[] // 用户组 postIds?: number[] // 岗位 expression?: string // 流程表达式 + userFieldOnForm?: string // 表单内用户字段 + deptFieldOnForm?: string // 表单内部门字段 approveRatio?: number rejectHandlerType?: RejectHandlerType returnNodeId?: string @@ -174,6 +169,8 @@ export type CopyTaskFormType = { userIds?: number[] // 用户 userGroups?: number[] // 用户组 postIds?: number[] // 岗位 + userFieldOnForm?: string // 表单内用户字段 + deptFieldOnForm?: string // 表单内部门字段 expression?: string // 流程表达式 } @@ -187,6 +184,7 @@ export function useNodeForm(nodeType: NodeType) { const deptOptions = inject>('deptList') // 部门列表 const userGroupOptions = inject>('userGroupList') // 用户组列表 const deptTreeOptions = inject('deptTree') // 部门树 + const formFields = inject>('formFields') // 流程表单字段 const configForm = ref() if (nodeType === NodeType.USER_TASK_NODE) { configForm.value = { @@ -282,6 +280,18 @@ export function useNodeForm(nodeType: NodeType) { } } + // 表单内用户字段 + if (configForm.value?.candidateStrategy === CandidateStrategy.USER_FIELD_ON_FORM) { + const formFieldOptions = parseFormCreateFields(unref(formFields)) + const item = formFieldOptions.find((item) => item.field === configForm.value?.userFieldOnForm) + showText = `表单用户:${item?.title}` + } + + // 表单内部门负责人 + if (configForm.value?.candidateStrategy === CandidateStrategy.DEPT_LEADER_ON_FORM) { + showText = `表单内部门负责人` + } + // 发起人自选 if (configForm.value?.candidateStrategy === CandidateStrategy.START_USER_SELECT) { showText = `发起人自选` @@ -328,6 +338,9 @@ export function useNodeForm(nodeType: NodeType) { case CandidateStrategy.USER_GROUP: candidateParam = configForm.value.userGroups!.join(',') break + case CandidateStrategy.USER_FIELD_ON_FORM: + candidateParam = configForm.value.userFieldOnForm! + break case CandidateStrategy.EXPRESSION: candidateParam = configForm.value.expression! break @@ -347,6 +360,13 @@ export function useNodeForm(nodeType: NodeType) { candidateParam = deptIds.concat('|' + configForm.value.deptLevel + '') break } + // 表单内部门的负责人 + case CandidateStrategy.DEPT_LEADER_ON_FORM: { + // 候选人参数格式: | 分隔 。左边为表单内部门字段。 右边为部门层级 + const deptFieldOnForm = configForm.value.deptFieldOnForm! + candidateParam = deptFieldOnForm.concat('|' + configForm.value.deptLevel + '') + break + } default: break } @@ -376,6 +396,9 @@ export function useNodeForm(nodeType: NodeType) { case CandidateStrategy.USER_GROUP: configForm.value.userGroups = candidateParam.split(',').map((item) => +item) break + case CandidateStrategy.USER_FIELD_ON_FORM: + configForm.value.userFieldOnForm = candidateParam + break case CandidateStrategy.EXPRESSION: configForm.value.expression = candidateParam break @@ -396,6 +419,14 @@ export function useNodeForm(nodeType: NodeType) { configForm.value.deptLevel = +paramArray[1] break } + // 表单内的部门负责人 + case CandidateStrategy.DEPT_LEADER_ON_FORM: { + // 候选人参数格式: | 分隔 。左边为表单内的部门字段。 右边为部门层级 + const paramArray = candidateParam.split('|') + configForm.value.deptFieldOnForm = paramArray[0] + configForm.value.deptLevel = +paramArray[1] + break + } default: break } diff --git a/src/components/SimpleProcessDesignerV2/src/nodes-config/CopyTaskNodeConfig.vue b/src/components/SimpleProcessDesignerV2/src/nodes-config/CopyTaskNodeConfig.vue index c3006afc..91ac5b87 100644 --- a/src/components/SimpleProcessDesignerV2/src/nodes-config/CopyTaskNodeConfig.vue +++ b/src/components/SimpleProcessDesignerV2/src/nodes-config/CopyTaskNodeConfig.vue @@ -60,7 +60,8 @@ - + + + + + + + + + + + + + + + { + let label = '部门负责人来源' + if (configForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) { + label = label + '(指定部门向上)' + } else { + label = label + '(发起人部门向上)' + } + return label +}) // 抽屉配置 const { settingVisible, closeDrawer, openDrawer } = useDrawer() // 当前节点 @@ -230,9 +288,21 @@ const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.COPY_ // 激活的 Tab 标签页 const activeTabName = ref('user') // 表单字段权限配置 -const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission( +const { formType, fieldsPermissionConfig, formFieldOptions, getNodeConfigFormFields } = useFormFieldsPermission( FieldPermissionType.READ ) +// 表单内用户字段选项, 必须是必填和用户选择器 +const userFieldOnFormOptions = computed(() => { + return formFieldOptions.filter( + (item) => item.required && item.type === 'UserSelect' + ) +}) +// 表单内部门字段选项, 必须是必填和部门选择器 +const deptFieldOnFormOptions = computed(() => { + return formFieldOptions.filter( + (item) => item.required && item.type === 'DeptSelect' + ) +}) // 抄送人表单配置 const formRef = ref() // 表单 Ref // 表单校验规则 @@ -243,6 +313,8 @@ const formRules = reactive({ deptIds: [{ required: true, message: '部门不能为空', trigger: 'change' }], userGroups: [{ required: true, message: '用户组不能为空', trigger: 'change' }], postIds: [{ required: true, message: '岗位不能为空', trigger: 'change' }], + userFieldOnForm: [{ required: true, message: '表单内用户字段不能为空', trigger: 'change' }], + deptFieldOnForm: [{ required: true, message: '表单内部门字段不能为空', trigger: 'change' }], expression: [{ required: true, message: '流程表达式不能为空', trigger: 'blur' }] }) @@ -272,6 +344,7 @@ const changeCandidateStrategy = () => { configForm.value.postIds = [] configForm.value.userGroups = [] configForm.value.deptLevel = 1 + configForm.value.userFieldOnForm = '' } // 保存配置 const saveConfig = async () => { diff --git a/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue b/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue index 899d7045..02ec582c 100644 --- a/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue +++ b/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue @@ -56,7 +56,6 @@ - - - - - - + + + + + + + + + + + + + + + { let label = '部门负责人来源' if (configForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) { label = label + '(指定部门向上)' + } else if (configForm.value.candidateStrategy == CandidateStrategy.DEPT_LEADER_ON_FORM) { + label = label + '(表单内部门向上)' } else { - label = label + '(发起人部门向上)' + label = label + '(发起人部门向上)' } return label }) @@ -482,9 +512,21 @@ const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.USER_ // 激活的 Tab 标签页 const activeTabName = ref('user') // 表单字段权限设置 -const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission( +const { formType, fieldsPermissionConfig, formFieldOptions, getNodeConfigFormFields } = useFormFieldsPermission( FieldPermissionType.READ ) +// 表单内用户字段选项, 必须是必填和用户选择器 +const userFieldOnFormOptions = computed(() => { + return formFieldOptions.filter( + (item) => item.required && item.type === 'UserSelect' + ) +}) +// 表单内部门字段选项, 必须是必填和部门选择器 +const deptFieldOnFormOptions = computed(() => { + return formFieldOptions.filter( + (item) => item.required && item.type === 'DeptSelect' + ) +}) // 操作按钮设置 const { buttonsSetting, btnDisplayNameEdit, changeBtnDisplayName, btnDisplayNameBlurEvent } = useButtonsSetting() @@ -498,6 +540,8 @@ const formRules = reactive({ roleIds: [{ required: true, message: '角色不能为空', trigger: 'change' }], deptIds: [{ required: true, message: '部门不能为空', trigger: 'change' }], userGroups: [{ required: true, message: '用户组不能为空', trigger: 'change' }], + userFieldOnForm: [{ required: true, message: '表单内用户字段不能为空', trigger: 'change' }], + deptFieldOnForm: [{ required: true, message: '表单内部门字段不能为空', trigger: 'change' }], postIds: [{ required: true, message: '岗位不能为空', trigger: 'change' }], expression: [{ required: true, message: '流程表达式不能为空', trigger: 'blur' }], approveMethod: [{ required: true, message: '多人审批方式不能为空', trigger: 'change' }], @@ -533,6 +577,8 @@ const changeCandidateStrategy = () => { configForm.value.postIds = [] configForm.value.userGroups = [] configForm.value.deptLevel = 1 + configForm.value.userFieldOnForm = '' + configForm.value.deptFieldOnForm = '' configForm.value.approveMethod = ApproveMethodType.SEQUENTIAL_APPROVE }