feat: [BPM 工作流] Simple 模型 - 抄送人节点
							parent
							
								
									f7edc78a59
								
							
						
					
					
						commit
						f679df0036
					
				|  | @ -0,0 +1,529 @@ | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import type { Rule } from 'ant-design-vue/es/form'; | ||||||
|  | 
 | ||||||
|  | import type { Ref } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import type { SimpleFlowNode } from '../../consts'; | ||||||
|  | import type { CopyTaskFormType } from '../../helpers'; | ||||||
|  | 
 | ||||||
|  | import { computed, onMounted, reactive, ref } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { useVbenDrawer } from '@vben/common-ui'; | ||||||
|  | import { IconifyIcon } from '@vben/icons'; | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   Button, | ||||||
|  |   Col, | ||||||
|  |   Form, | ||||||
|  |   FormItem, | ||||||
|  |   Input, | ||||||
|  |   Radio, | ||||||
|  |   RadioGroup, | ||||||
|  |   Row, | ||||||
|  |   Select, | ||||||
|  |   SelectOption, | ||||||
|  |   TabPane, | ||||||
|  |   Tabs, | ||||||
|  |   Textarea, | ||||||
|  |   TreeSelect, | ||||||
|  | } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { BpmModelFormType } from '#/utils'; | ||||||
|  | 
 | ||||||
|  | import { | ||||||
|  |   CANDIDATE_STRATEGY, | ||||||
|  |   CandidateStrategy, | ||||||
|  |   FieldPermissionType, | ||||||
|  |   MULTI_LEVEL_DEPT, | ||||||
|  |   NodeType, | ||||||
|  | } from '../../consts'; | ||||||
|  | import { | ||||||
|  |   useFormFieldsPermission, | ||||||
|  |   useNodeForm, | ||||||
|  |   useNodeName, | ||||||
|  |   useWatchNode, | ||||||
|  | } from '../../helpers'; | ||||||
|  | 
 | ||||||
|  | defineOptions({ | ||||||
|  |   name: 'CopyTaskNodeConfig', | ||||||
|  | }); | ||||||
|  | const props = defineProps({ | ||||||
|  |   flowNode: { | ||||||
|  |     type: Object as () => SimpleFlowNode, | ||||||
|  |     required: true, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | const deptLevelLabel = computed(() => { | ||||||
|  |   let label = '部门负责人来源'; | ||||||
|  |   label = | ||||||
|  |     configForm.value.candidateStrategy === | ||||||
|  |     CandidateStrategy.MULTI_LEVEL_DEPT_LEADER | ||||||
|  |       ? `${label}(指定部门向上)` | ||||||
|  |       : `${label}(发起人部门向上)`; | ||||||
|  |   return label; | ||||||
|  | }); | ||||||
|  | // 抽屉配置 | ||||||
|  | const [Drawer, drawerApi] = useVbenDrawer({ | ||||||
|  |   header: true, | ||||||
|  |   closable: true, | ||||||
|  |   title: '', | ||||||
|  |   placement: 'right', | ||||||
|  |   onCancel() { | ||||||
|  |     drawerApi.close(); | ||||||
|  |   }, | ||||||
|  |   onConfirm() { | ||||||
|  |     saveConfig(); | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | // 当前节点 | ||||||
|  | const currentNode = useWatchNode(props); | ||||||
|  | // 节点名称 | ||||||
|  | const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( | ||||||
|  |   NodeType.COPY_TASK_NODE, | ||||||
|  | ); | ||||||
|  | // 激活的 Tab 标签页 | ||||||
|  | const activeTabName = ref('user'); | ||||||
|  | // 表单字段权限配置 | ||||||
|  | const { | ||||||
|  |   formType, | ||||||
|  |   fieldsPermissionConfig, | ||||||
|  |   formFieldOptions, | ||||||
|  |   getNodeConfigFormFields, | ||||||
|  | } = useFormFieldsPermission(FieldPermissionType.READ); | ||||||
|  | // 表单内用户字段选项, 必须是必填和用户选择器 | ||||||
|  | const userFieldOnFormOptions = computed(() => { | ||||||
|  |   return formFieldOptions.filter((item) => item.type === 'UserSelect'); | ||||||
|  | }); | ||||||
|  | // 表单内部门字段选项, 必须是必填和部门选择器 | ||||||
|  | const deptFieldOnFormOptions = computed(() => { | ||||||
|  |   return formFieldOptions.filter((item) => item.type === 'DeptSelect'); | ||||||
|  | }); | ||||||
|  | // 抄送人表单配置 | ||||||
|  | const formRef = ref(); // 表单 Ref | ||||||
|  | // 表单校验规则 | ||||||
|  | const formRules: Record<string, Rule[]> = reactive({ | ||||||
|  |   candidateStrategy: [ | ||||||
|  |     { required: true, message: '抄送人设置不能为空', trigger: 'change' }, | ||||||
|  |   ], | ||||||
|  |   userIds: [{ required: true, message: '用户不能为空', trigger: 'change' }], | ||||||
|  |   roleIds: [{ required: true, message: '角色不能为空', trigger: 'change' }], | ||||||
|  |   deptIds: [{ required: true, message: '部门不能为空', trigger: 'change' }], | ||||||
|  |   userGroups: [ | ||||||
|  |     { required: true, message: '用户组不能为空', trigger: 'change' }, | ||||||
|  |   ], | ||||||
|  |   postIds: [{ required: true, message: '岗位不能为空', trigger: 'change' }], | ||||||
|  |   formUser: [ | ||||||
|  |     { required: true, message: '表单内用户字段不能为空', trigger: 'change' }, | ||||||
|  |   ], | ||||||
|  |   formDept: [ | ||||||
|  |     { required: true, message: '表单内部门字段不能为空', trigger: 'change' }, | ||||||
|  |   ], | ||||||
|  |   expression: [ | ||||||
|  |     { required: true, message: '流程表达式不能为空', trigger: 'blur' }, | ||||||
|  |   ], | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const { | ||||||
|  |   configForm: tempConfigForm, | ||||||
|  |   roleOptions, | ||||||
|  |   postOptions, | ||||||
|  |   userOptions, | ||||||
|  |   userGroupOptions, | ||||||
|  |   deptTreeOptions, | ||||||
|  |   getShowText, | ||||||
|  |   handleCandidateParam, | ||||||
|  |   parseCandidateParam, | ||||||
|  | } = useNodeForm(NodeType.COPY_TASK_NODE); | ||||||
|  | const configForm = tempConfigForm as Ref<CopyTaskFormType>; | ||||||
|  | // 抄送人策略, 去掉发起人自选 和 发起人自己 | ||||||
|  | const copyUserStrategies = computed(() => { | ||||||
|  |   return CANDIDATE_STRATEGY.filter( | ||||||
|  |     (item) => item.value !== CandidateStrategy.START_USER, | ||||||
|  |   ); | ||||||
|  | }); | ||||||
|  | // 改变抄送人设置策略 | ||||||
|  | const changeCandidateStrategy = () => { | ||||||
|  |   configForm.value.userIds = []; | ||||||
|  |   configForm.value.deptIds = []; | ||||||
|  |   configForm.value.roleIds = []; | ||||||
|  |   configForm.value.postIds = []; | ||||||
|  |   configForm.value.userGroups = []; | ||||||
|  |   configForm.value.deptLevel = 1; | ||||||
|  |   configForm.value.formUser = ''; | ||||||
|  |   configForm.value.formDept = ''; | ||||||
|  | }; | ||||||
|  | // 保存配置 | ||||||
|  | const saveConfig = async () => { | ||||||
|  |   activeTabName.value = 'user'; | ||||||
|  |   if (!formRef.value) 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.candidateParam = handleCandidateParam(); | ||||||
|  |   currentNode.value.candidateStrategy = configForm.value.candidateStrategy; | ||||||
|  |   currentNode.value.showText = showText; | ||||||
|  |   currentNode.value.fieldsPermission = fieldsPermissionConfig.value; | ||||||
|  |   drawerApi.close(); | ||||||
|  |   return true; | ||||||
|  | }; | ||||||
|  | // 显示抄送节点配置, 由父组件传过来 | ||||||
|  | const showCopyTaskNodeConfig = (node: SimpleFlowNode) => { | ||||||
|  |   nodeName.value = node.name; | ||||||
|  |   // 抄送人设置 | ||||||
|  |   configForm.value.candidateStrategy = node.candidateStrategy!; | ||||||
|  |   parseCandidateParam(node.candidateStrategy!, node?.candidateParam); | ||||||
|  |   // 表单字段权限 | ||||||
|  |   getNodeConfigFormFields(node.fieldsPermission); | ||||||
|  | 
 | ||||||
|  |   drawerApi.open(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** 批量更新权限 */ | ||||||
|  | const updatePermission = (type: string) => { | ||||||
|  |   fieldsPermissionConfig.value.forEach((field) => { | ||||||
|  |     if (type === 'READ') { | ||||||
|  |       field.permission = FieldPermissionType.READ; | ||||||
|  |     } else if (type === 'WRITE') { | ||||||
|  |       field.permission = FieldPermissionType.WRITE; | ||||||
|  |     } else { | ||||||
|  |       field.permission = FieldPermissionType.NONE; | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 在组件初始化时对表单字段进行处理 | ||||||
|  | onMounted(() => { | ||||||
|  |   // 可以在这里进行初始化操作 | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件 | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <Drawer class="w-[580px]"> | ||||||
|  |     <template #title> | ||||||
|  |       <div class="config-header"> | ||||||
|  |         <Input | ||||||
|  |           v-if="showInput" | ||||||
|  |           type="text" | ||||||
|  |           class="config-editable-input" | ||||||
|  |           @blur="blurEvent()" | ||||||
|  |           v-model:value="nodeName" | ||||||
|  |           :placeholder="nodeName" | ||||||
|  |         /> | ||||||
|  |         <div v-else class="node-name"> | ||||||
|  |           {{ nodeName }} | ||||||
|  |           <IconifyIcon class="ml-1" icon="ep:edit-pen" @click="clickIcon()" /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </template> | ||||||
|  |     <Tabs v-model:active-key="activeTabName"> | ||||||
|  |       <TabPane tab="抄送人" key="user"> | ||||||
|  |         <div> | ||||||
|  |           <Form | ||||||
|  |             ref="formRef" | ||||||
|  |             :model="configForm" | ||||||
|  |             :label-col="{ span: 24 }" | ||||||
|  |             :wrapper-col="{ span: 24 }" | ||||||
|  |             :rules="formRules" | ||||||
|  |           > | ||||||
|  |             <FormItem label="抄送人设置" name="candidateStrategy"> | ||||||
|  |               <RadioGroup | ||||||
|  |                 v-model:value="configForm.candidateStrategy" | ||||||
|  |                 @change="changeCandidateStrategy" | ||||||
|  |               > | ||||||
|  |                 <Row :gutter="[0, 8]"> | ||||||
|  |                   <Col | ||||||
|  |                     v-for="(dict, index) in copyUserStrategies" | ||||||
|  |                     :key="index" | ||||||
|  |                     :span="8" | ||||||
|  |                   > | ||||||
|  |                     <Radio :value="dict.value" :label="dict.value"> | ||||||
|  |                       {{ dict.label }} | ||||||
|  |                     </Radio> | ||||||
|  |                   </Col> | ||||||
|  |                 </Row> | ||||||
|  |               </RadioGroup> | ||||||
|  |             </FormItem> | ||||||
|  | 
 | ||||||
|  |             <FormItem | ||||||
|  |               v-if="configForm.candidateStrategy === CandidateStrategy.ROLE" | ||||||
|  |               label="指定角色" | ||||||
|  |               name="roleIds" | ||||||
|  |             > | ||||||
|  |               <Select | ||||||
|  |                 v-model:value="configForm.roleIds" | ||||||
|  |                 clearable | ||||||
|  |                 mode="multiple" | ||||||
|  |               > | ||||||
|  |                 <SelectOption | ||||||
|  |                   v-for="item in roleOptions" | ||||||
|  |                   :key="item.id" | ||||||
|  |                   :label="item.name" | ||||||
|  |                   :value="item.id" | ||||||
|  |                 > | ||||||
|  |                   {{ item.name }} | ||||||
|  |                 </SelectOption> | ||||||
|  |               </Select> | ||||||
|  |             </FormItem> | ||||||
|  |             <FormItem | ||||||
|  |               v-if=" | ||||||
|  |                 configForm.candidateStrategy === | ||||||
|  |                   CandidateStrategy.DEPT_MEMBER || | ||||||
|  |                 configForm.candidateStrategy === | ||||||
|  |                   CandidateStrategy.DEPT_LEADER || | ||||||
|  |                 configForm.candidateStrategy === | ||||||
|  |                   CandidateStrategy.MULTI_LEVEL_DEPT_LEADER | ||||||
|  |               " | ||||||
|  |               label="指定部门" | ||||||
|  |               name="deptIds" | ||||||
|  |             > | ||||||
|  |               <TreeSelect | ||||||
|  |                 v-model:value="configForm.deptIds" | ||||||
|  |                 :tree-data="deptTreeOptions" | ||||||
|  |                 :field-names="{ | ||||||
|  |                   label: 'name', | ||||||
|  |                   value: 'id', | ||||||
|  |                   children: 'children', | ||||||
|  |                 }" | ||||||
|  |                 empty-text="加载中,请稍后" | ||||||
|  |                 multiple | ||||||
|  |                 :check-strictly="true" | ||||||
|  |                 allow-clear | ||||||
|  |                 tree-checkable | ||||||
|  |               /> | ||||||
|  |             </FormItem> | ||||||
|  |             <FormItem | ||||||
|  |               v-if="configForm.candidateStrategy === CandidateStrategy.POST" | ||||||
|  |               label="指定岗位" | ||||||
|  |               name="postIds" | ||||||
|  |             > | ||||||
|  |               <Select | ||||||
|  |                 v-model:value="configForm.postIds" | ||||||
|  |                 clearable | ||||||
|  |                 mode="multiple" | ||||||
|  |               > | ||||||
|  |                 <SelectOption | ||||||
|  |                   v-for="item in postOptions" | ||||||
|  |                   :key="item.id" | ||||||
|  |                   :label="item.name" | ||||||
|  |                   :value="item.id!" | ||||||
|  |                 > | ||||||
|  |                   {{ item.name }} | ||||||
|  |                 </SelectOption> | ||||||
|  |               </Select> | ||||||
|  |             </FormItem> | ||||||
|  |             <FormItem | ||||||
|  |               v-if="configForm.candidateStrategy === CandidateStrategy.USER" | ||||||
|  |               label="指定用户" | ||||||
|  |               name="userIds" | ||||||
|  |             > | ||||||
|  |               <Select | ||||||
|  |                 v-model:value="configForm.userIds" | ||||||
|  |                 clearable | ||||||
|  |                 mode="multiple" | ||||||
|  |               > | ||||||
|  |                 <SelectOption | ||||||
|  |                   v-for="item in userOptions" | ||||||
|  |                   :key="item.id" | ||||||
|  |                   :label="item.nickname" | ||||||
|  |                   :value="item.id" | ||||||
|  |                 > | ||||||
|  |                   {{ item.nickname }} | ||||||
|  |                 </SelectOption> | ||||||
|  |               </Select> | ||||||
|  |             </FormItem> | ||||||
|  |             <FormItem | ||||||
|  |               v-if=" | ||||||
|  |                 configForm.candidateStrategy === CandidateStrategy.USER_GROUP | ||||||
|  |               " | ||||||
|  |               label="指定用户组" | ||||||
|  |               name="userGroups" | ||||||
|  |             > | ||||||
|  |               <Select | ||||||
|  |                 v-model:value="configForm.userGroups" | ||||||
|  |                 clearable | ||||||
|  |                 mode="multiple" | ||||||
|  |               > | ||||||
|  |                 <SelectOption | ||||||
|  |                   v-for="item in userGroupOptions" | ||||||
|  |                   :key="item.id" | ||||||
|  |                   :label="item.name" | ||||||
|  |                   :value="item.id" | ||||||
|  |                 > | ||||||
|  |                   {{ item.name }} | ||||||
|  |                 </SelectOption> | ||||||
|  |               </Select> | ||||||
|  |             </FormItem> | ||||||
|  |             <FormItem | ||||||
|  |               v-if=" | ||||||
|  |                 configForm.candidateStrategy === CandidateStrategy.FORM_USER | ||||||
|  |               " | ||||||
|  |               label="表单内用户字段" | ||||||
|  |               name="formUser" | ||||||
|  |             > | ||||||
|  |               <Select v-model:value="configForm.formUser" clearable> | ||||||
|  |                 <SelectOption | ||||||
|  |                   v-for="(item, idx) in userFieldOnFormOptions" | ||||||
|  |                   :key="idx" | ||||||
|  |                   :label="item.title" | ||||||
|  |                   :value="item.field" | ||||||
|  |                   :disabled="!item.required" | ||||||
|  |                 > | ||||||
|  |                   {{ item.title }} | ||||||
|  |                 </SelectOption> | ||||||
|  |               </Select> | ||||||
|  |             </FormItem> | ||||||
|  |             <FormItem | ||||||
|  |               v-if=" | ||||||
|  |                 configForm.candidateStrategy === | ||||||
|  |                 CandidateStrategy.FORM_DEPT_LEADER | ||||||
|  |               " | ||||||
|  |               label="表单内部门字段" | ||||||
|  |               name="formDept" | ||||||
|  |             > | ||||||
|  |               <Select v-model:value="configForm.formDept" clearable> | ||||||
|  |                 <SelectOption | ||||||
|  |                   v-for="(item, idx) in deptFieldOnFormOptions" | ||||||
|  |                   :key="idx" | ||||||
|  |                   :label="item.title" | ||||||
|  |                   :value="item.field" | ||||||
|  |                   :disabled="!item.required" | ||||||
|  |                 > | ||||||
|  |                   {{ item.title }} | ||||||
|  |                 </SelectOption> | ||||||
|  |               </Select> | ||||||
|  |             </FormItem> | ||||||
|  |             <FormItem | ||||||
|  |               v-if=" | ||||||
|  |                 configForm.candidateStrategy === | ||||||
|  |                   CandidateStrategy.MULTI_LEVEL_DEPT_LEADER || | ||||||
|  |                 configForm.candidateStrategy === | ||||||
|  |                   CandidateStrategy.START_USER_DEPT_LEADER || | ||||||
|  |                 configForm.candidateStrategy === | ||||||
|  |                   CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER || | ||||||
|  |                 configForm.candidateStrategy === | ||||||
|  |                   CandidateStrategy.FORM_DEPT_LEADER | ||||||
|  |               " | ||||||
|  |               :label="deptLevelLabel!" | ||||||
|  |               name="deptLevel" | ||||||
|  |             > | ||||||
|  |               <Select v-model:value="configForm.deptLevel" clearable> | ||||||
|  |                 <SelectOption | ||||||
|  |                   v-for="(item, index) in MULTI_LEVEL_DEPT" | ||||||
|  |                   :key="index" | ||||||
|  |                   :label="item.label" | ||||||
|  |                   :value="item.value" | ||||||
|  |                 > | ||||||
|  |                   {{ item.label }} | ||||||
|  |                 </SelectOption> | ||||||
|  |               </Select> | ||||||
|  |             </FormItem> | ||||||
|  |             <FormItem | ||||||
|  |               v-if=" | ||||||
|  |                 configForm.candidateStrategy === CandidateStrategy.EXPRESSION | ||||||
|  |               " | ||||||
|  |               label="流程表达式" | ||||||
|  |               name="expression" | ||||||
|  |             > | ||||||
|  |               <Textarea v-model:value="configForm.expression" clearable /> | ||||||
|  |             </FormItem> | ||||||
|  |           </Form> | ||||||
|  |         </div> | ||||||
|  |       </TabPane> | ||||||
|  |       <TabPane | ||||||
|  |         tab="表单字段权限" | ||||||
|  |         key="fields" | ||||||
|  |         v-if="formType === BpmModelFormType.NORMAL" | ||||||
|  |       > | ||||||
|  |         <div class="p-1"> | ||||||
|  |           <div class="mb-4 text-[16px] font-bold">字段权限</div> | ||||||
|  | 
 | ||||||
|  |           <!-- 表头 --> | ||||||
|  |           <Row class="border border-gray-200 px-4 py-3"> | ||||||
|  |             <Col :span="8" class="font-bold">字段名称</Col> | ||||||
|  |             <Col :span="16"> | ||||||
|  |               <Row> | ||||||
|  |                 <Col :span="8" class="flex items-center justify-center"> | ||||||
|  |                   <span | ||||||
|  |                     class="cursor-pointer font-bold" | ||||||
|  |                     @click="updatePermission('READ')" | ||||||
|  |                   > | ||||||
|  |                     只读 | ||||||
|  |                   </span> | ||||||
|  |                 </Col> | ||||||
|  |                 <Col :span="8" class="flex items-center justify-center"> | ||||||
|  |                   <span | ||||||
|  |                     class="cursor-pointer font-bold" | ||||||
|  |                     @click="updatePermission('WRITE')" | ||||||
|  |                   > | ||||||
|  |                     可编辑 | ||||||
|  |                   </span> | ||||||
|  |                 </Col> | ||||||
|  |                 <Col :span="8" class="flex items-center justify-center"> | ||||||
|  |                   <span | ||||||
|  |                     class="cursor-pointer font-bold" | ||||||
|  |                     @click="updatePermission('NONE')" | ||||||
|  |                   > | ||||||
|  |                     隐藏 | ||||||
|  |                   </span> | ||||||
|  |                 </Col> | ||||||
|  |               </Row> | ||||||
|  |             </Col> | ||||||
|  |           </Row> | ||||||
|  | 
 | ||||||
|  |           <!-- 表格内容 --> | ||||||
|  |           <div v-for="(item, index) in fieldsPermissionConfig" :key="index"> | ||||||
|  |             <Row class="border border-t-0 border-gray-200 px-4 py-2"> | ||||||
|  |               <Col :span="8" class="flex items-center truncate"> | ||||||
|  |                 {{ item.title }} | ||||||
|  |               </Col> | ||||||
|  |               <Col :span="16"> | ||||||
|  |                 <RadioGroup v-model:value="item.permission" class="w-full"> | ||||||
|  |                   <Row> | ||||||
|  |                     <Col :span="8" class="flex items-center justify-center"> | ||||||
|  |                       <Radio | ||||||
|  |                         :value="FieldPermissionType.READ" | ||||||
|  |                         size="large" | ||||||
|  |                         :label="FieldPermissionType.READ" | ||||||
|  |                       /> | ||||||
|  |                     </Col> | ||||||
|  |                     <Col :span="8" class="flex items-center justify-center"> | ||||||
|  |                       <Radio | ||||||
|  |                         :value="FieldPermissionType.WRITE" | ||||||
|  |                         size="large" | ||||||
|  |                         :label="FieldPermissionType.WRITE" | ||||||
|  |                         disabled | ||||||
|  |                       /> | ||||||
|  |                     </Col> | ||||||
|  |                     <Col :span="8" class="flex items-center justify-center"> | ||||||
|  |                       <Radio | ||||||
|  |                         :value="FieldPermissionType.NONE" | ||||||
|  |                         size="large" | ||||||
|  |                         :label="FieldPermissionType.NONE" | ||||||
|  |                       /> | ||||||
|  |                     </Col> | ||||||
|  |                   </Row> | ||||||
|  |                 </RadioGroup> | ||||||
|  |               </Col> | ||||||
|  |             </Row> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </TabPane> | ||||||
|  |     </Tabs> | ||||||
|  |     <template #footer> | ||||||
|  |       <Button type="primary" @click="saveConfig">确 定</Button> | ||||||
|  |       <Button @click="drawerApi.close()">取 消</Button> | ||||||
|  |     </template> | ||||||
|  |   </Drawer> | ||||||
|  | </template> | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .config-editable-input { | ||||||
|  |   &:focus { | ||||||
|  |     outline: 0; | ||||||
|  |     border-color: #40a9ff; | ||||||
|  |     box-shadow: 0 0 0 2px rgb(24 144 255 / 20%); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -34,6 +34,8 @@ import { | ||||||
|   TypographyText, |   TypographyText, | ||||||
| } from 'ant-design-vue'; | } from 'ant-design-vue'; | ||||||
| 
 | 
 | ||||||
|  | import { BpmModelFormType } from '#/utils'; | ||||||
|  | 
 | ||||||
| import { | import { | ||||||
|   APPROVE_METHODS, |   APPROVE_METHODS, | ||||||
|   APPROVE_TYPE, |   APPROVE_TYPE, | ||||||
|  | @ -626,11 +628,9 @@ onMounted(() => { | ||||||
|               name="roleIds" |               name="roleIds" | ||||||
|             > |             > | ||||||
|               <Select |               <Select | ||||||
|                 filterable |  | ||||||
|                 v-model:value="configForm.roleIds" |                 v-model:value="configForm.roleIds" | ||||||
|                 clearable |                 clearable | ||||||
|                 mode="multiple" |                 mode="multiple" | ||||||
|                 style="width: 100%" |  | ||||||
|               > |               > | ||||||
|                 <SelectOption |                 <SelectOption | ||||||
|                   v-for="item in roleOptions" |                   v-for="item in roleOptions" | ||||||
|  | @ -664,10 +664,8 @@ onMounted(() => { | ||||||
|                 }" |                 }" | ||||||
|                 empty-text="加载中,请稍后" |                 empty-text="加载中,请稍后" | ||||||
|                 multiple |                 multiple | ||||||
|                 node-key="id" |  | ||||||
|                 :check-strictly="true" |                 :check-strictly="true" | ||||||
|                 allow-clear |                 allow-clear | ||||||
|                 style="width: 100%" |  | ||||||
|                 tree-checkable |                 tree-checkable | ||||||
|               /> |               /> | ||||||
|             </FormItem> |             </FormItem> | ||||||
|  | @ -677,11 +675,9 @@ onMounted(() => { | ||||||
|               name="postIds" |               name="postIds" | ||||||
|             > |             > | ||||||
|               <Select |               <Select | ||||||
|                 filterable |  | ||||||
|                 v-model:value="configForm.postIds" |                 v-model:value="configForm.postIds" | ||||||
|                 clearable |                 clearable | ||||||
|                 mode="multiple" |                 mode="multiple" | ||||||
|                 style="width: 100%" |  | ||||||
|               > |               > | ||||||
|                 <SelectOption |                 <SelectOption | ||||||
|                   v-for="item in postOptions" |                   v-for="item in postOptions" | ||||||
|  | @ -699,11 +695,9 @@ onMounted(() => { | ||||||
|               name="userIds" |               name="userIds" | ||||||
|             > |             > | ||||||
|               <Select |               <Select | ||||||
|                 filterable |  | ||||||
|                 v-model:value="configForm.userIds" |                 v-model:value="configForm.userIds" | ||||||
|                 clearable |                 clearable | ||||||
|                 mode="multiple" |                 mode="multiple" | ||||||
|                 style="width: 100%" |  | ||||||
|               > |               > | ||||||
|                 <SelectOption |                 <SelectOption | ||||||
|                   v-for="item in userOptions" |                   v-for="item in userOptions" | ||||||
|  | @ -723,11 +717,9 @@ onMounted(() => { | ||||||
|               name="userGroups" |               name="userGroups" | ||||||
|             > |             > | ||||||
|               <Select |               <Select | ||||||
|                 filterable |  | ||||||
|                 v-model:value="configForm.userGroups" |                 v-model:value="configForm.userGroups" | ||||||
|                 clearable |                 clearable | ||||||
|                 mode="multiple" |                 mode="multiple" | ||||||
|                 style="width: 100%" |  | ||||||
|               > |               > | ||||||
|                 <SelectOption |                 <SelectOption | ||||||
|                   v-for="item in userGroupOptions" |                   v-for="item in userGroupOptions" | ||||||
|  | @ -746,12 +738,7 @@ onMounted(() => { | ||||||
|               label="表单内用户字段" |               label="表单内用户字段" | ||||||
|               name="formUser" |               name="formUser" | ||||||
|             > |             > | ||||||
|               <Select |               <Select v-model:value="configForm.formUser" clearable> | ||||||
|                 filterable |  | ||||||
|                 v-model:value="configForm.formUser" |  | ||||||
|                 clearable |  | ||||||
|                 style="width: 100%" |  | ||||||
|               > |  | ||||||
|                 <SelectOption |                 <SelectOption | ||||||
|                   v-for="(item, idx) in userFieldOnFormOptions" |                   v-for="(item, idx) in userFieldOnFormOptions" | ||||||
|                   :key="idx" |                   :key="idx" | ||||||
|  | @ -771,12 +758,7 @@ onMounted(() => { | ||||||
|               label="表单内部门字段" |               label="表单内部门字段" | ||||||
|               name="formDept" |               name="formDept" | ||||||
|             > |             > | ||||||
|               <Select |               <Select v-model:value="configForm.formDept" clearable> | ||||||
|                 filterable |  | ||||||
|                 v-model:value="configForm.formDept" |  | ||||||
|                 clearable |  | ||||||
|                 style="width: 100%" |  | ||||||
|               > |  | ||||||
|                 <SelectOption |                 <SelectOption | ||||||
|                   v-for="(item, idx) in deptFieldOnFormOptions" |                   v-for="(item, idx) in deptFieldOnFormOptions" | ||||||
|                   :key="idx" |                   :key="idx" | ||||||
|  | @ -802,7 +784,7 @@ onMounted(() => { | ||||||
|               :label="deptLevelLabel!" |               :label="deptLevelLabel!" | ||||||
|               name="deptLevel" |               name="deptLevel" | ||||||
|             > |             > | ||||||
|               <Select filterable v-model:value="configForm.deptLevel" clearable> |               <Select v-model:value="configForm.deptLevel" clearable> | ||||||
|                 <SelectOption |                 <SelectOption | ||||||
|                   v-for="(item, index) in MULTI_LEVEL_DEPT" |                   v-for="(item, index) in MULTI_LEVEL_DEPT" | ||||||
|                   :key="index" |                   :key="index" | ||||||
|  | @ -821,11 +803,7 @@ onMounted(() => { | ||||||
|               label="流程表达式" |               label="流程表达式" | ||||||
|               name="expression" |               name="expression" | ||||||
|             > |             > | ||||||
|               <Textarea |               <Textarea v-model:value="configForm.expression" clearable /> | ||||||
|                 v-model:value="configForm.expression" |  | ||||||
|                 clearable |  | ||||||
|                 style="width: 100%" |  | ||||||
|               /> |  | ||||||
|             </FormItem> |             </FormItem> | ||||||
|             <!-- 多人审批/办理 方式 --> |             <!-- 多人审批/办理 方式 --> | ||||||
|             <FormItem :label="`多人${nodeTypeName}方式`" name="approveMethod"> |             <FormItem :label="`多人${nodeTypeName}方式`" name="approveMethod"> | ||||||
|  | @ -890,12 +868,7 @@ onMounted(() => { | ||||||
|                 label="驳回节点" |                 label="驳回节点" | ||||||
|                 name="returnNodeId" |                 name="returnNodeId" | ||||||
|               > |               > | ||||||
|                 <Select |                 <Select v-model:value="configForm.returnNodeId" clearable> | ||||||
|                   filterable |  | ||||||
|                   v-model:value="configForm.returnNodeId" |  | ||||||
|                   clearable |  | ||||||
|                   style="width: 100%" |  | ||||||
|                 > |  | ||||||
|                   <SelectOption |                   <SelectOption | ||||||
|                     v-for="item in returnTaskList" |                     v-for="item in returnTaskList" | ||||||
|                     :key="item.id" |                     :key="item.id" | ||||||
|  | @ -963,8 +936,7 @@ onMounted(() => { | ||||||
|                   <Col> |                   <Col> | ||||||
|                     <FormItem name="timeDuration"> |                     <FormItem name="timeDuration"> | ||||||
|                       <InputNumber |                       <InputNumber | ||||||
|                         class="mr-2" |                         class="mr-2 mt-0.5" | ||||||
|                         :style="{ width: '100px' }" |  | ||||||
|                         v-model:value="configForm.timeDuration" |                         v-model:value="configForm.timeDuration" | ||||||
|                         :min="1" |                         :min="1" | ||||||
|                         controls-position="right" |                         controls-position="right" | ||||||
|  | @ -973,7 +945,6 @@ onMounted(() => { | ||||||
|                   </Col> |                   </Col> | ||||||
|                   <Col> |                   <Col> | ||||||
|                     <Select |                     <Select | ||||||
|                       filterable |  | ||||||
|                       v-model:value="timeUnit" |                       v-model:value="timeUnit" | ||||||
|                       class="mr-2" |                       class="mr-2" | ||||||
|                       :style="{ width: '100px' }" |                       :style="{ width: '100px' }" | ||||||
|  | @ -1040,11 +1011,9 @@ onMounted(() => { | ||||||
|               name="assignEmptyHandlerUserIds" |               name="assignEmptyHandlerUserIds" | ||||||
|             > |             > | ||||||
|               <Select |               <Select | ||||||
|                 filterable |  | ||||||
|                 v-model:value="configForm.assignEmptyHandlerUserIds" |                 v-model:value="configForm.assignEmptyHandlerUserIds" | ||||||
|                 clearable |                 clearable | ||||||
|                 mode="multiple" |                 mode="multiple" | ||||||
|                 style="width: 100%" |  | ||||||
|               > |               > | ||||||
|                 <SelectOption |                 <SelectOption | ||||||
|                   v-for="item in userOptions" |                   v-for="item in userOptions" | ||||||
|  | @ -1151,7 +1120,11 @@ onMounted(() => { | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </TabPane> |       </TabPane> | ||||||
|       <TabPane tab="表单字段权限" key="fields" v-if="formType === 10"> |       <TabPane | ||||||
|  |         tab="表单字段权限" | ||||||
|  |         key="fields" | ||||||
|  |         v-if="formType === BpmModelFormType.NORMAL" | ||||||
|  |       > | ||||||
|         <div class="p-1"> |         <div class="p-1"> | ||||||
|           <div class="mb-4 text-[16px] font-bold">字段权限</div> |           <div class="mb-4 text-[16px] font-bold">字段权限</div> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,118 @@ | ||||||
|  | <script setup lang="ts"> | ||||||
|  | import type { SimpleFlowNode } from '../../consts'; | ||||||
|  | 
 | ||||||
|  | import { inject, ref } from 'vue'; | ||||||
|  | 
 | ||||||
|  | import { IconifyIcon } from '@vben/icons'; | ||||||
|  | 
 | ||||||
|  | import { Input } from 'ant-design-vue'; | ||||||
|  | 
 | ||||||
|  | import { NODE_DEFAULT_TEXT, NodeType } from '../../consts'; | ||||||
|  | import { useNodeName2, useTaskStatusClass, useWatchNode } from '../../helpers'; | ||||||
|  | import CopyTaskNodeConfig from '../nodes-config/copy-task-node-config.vue'; | ||||||
|  | import NodeHandler from './node-handler.vue'; | ||||||
|  | 
 | ||||||
|  | defineOptions({ | ||||||
|  |   name: 'CopyTaskNode', | ||||||
|  | }); | ||||||
|  | const props = defineProps({ | ||||||
|  |   flowNode: { | ||||||
|  |     type: Object as () => SimpleFlowNode, | ||||||
|  |     required: true, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | // 定义事件,更新父组件。 | ||||||
|  | const emits = defineEmits<{ | ||||||
|  |   'update:flowNode': [node: SimpleFlowNode | undefined]; | ||||||
|  | }>(); | ||||||
|  | // 是否只读 | ||||||
|  | const readonly = inject<Boolean>('readonly'); | ||||||
|  | // 监控节点的变化 | ||||||
|  | const currentNode = useWatchNode(props); | ||||||
|  | // 节点名称编辑 | ||||||
|  | const { showInput, blurEvent, clickTitle } = useNodeName2( | ||||||
|  |   currentNode, | ||||||
|  |   NodeType.COPY_TASK_NODE, | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | const nodeSetting = ref(); | ||||||
|  | // 打开节点配置 | ||||||
|  | const openNodeConfig = () => { | ||||||
|  |   if (readonly) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   nodeSetting.value.showCopyTaskNodeConfig(currentNode.value); | ||||||
|  |   nodeSetting.value.openDrawer(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // 删除节点。更新当前节点为孩子节点 | ||||||
|  | const deleteNode = () => { | ||||||
|  |   emits('update:flowNode', currentNode.value.childNode); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <div class="node-wrapper"> | ||||||
|  |     <div class="node-container"> | ||||||
|  |       <div | ||||||
|  |         class="node-box" | ||||||
|  |         :class="[ | ||||||
|  |           { 'node-config-error': !currentNode.showText }, | ||||||
|  |           `${useTaskStatusClass(currentNode?.activityStatus)}`, | ||||||
|  |         ]" | ||||||
|  |       > | ||||||
|  |         <div class="node-title-container"> | ||||||
|  |           <div class="node-title-icon copy-task"> | ||||||
|  |             <span class="iconfont icon-copy"></span> | ||||||
|  |           </div> | ||||||
|  |           <Input | ||||||
|  |             v-if="!readonly && showInput" | ||||||
|  |             type="text" | ||||||
|  |             class="editable-title-input" | ||||||
|  |             @blur="blurEvent()" | ||||||
|  |             v-model="currentNode.name" | ||||||
|  |             :placeholder="currentNode.name" | ||||||
|  |           /> | ||||||
|  |           <div v-else class="node-title" @click="clickTitle"> | ||||||
|  |             {{ currentNode.name }} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="node-content" @click="openNodeConfig"> | ||||||
|  |           <div | ||||||
|  |             class="node-text" | ||||||
|  |             :title="currentNode.showText" | ||||||
|  |             v-if="currentNode.showText" | ||||||
|  |           > | ||||||
|  |             {{ currentNode.showText }} | ||||||
|  |           </div> | ||||||
|  |           <div class="node-text" v-else> | ||||||
|  |             {{ NODE_DEFAULT_TEXT.get(NodeType.COPY_TASK_NODE) }} | ||||||
|  |           </div> | ||||||
|  |           <IconifyIcon v-if="!readonly" icon="ep:arrow-right-bold" /> | ||||||
|  |         </div> | ||||||
|  |         <div v-if="!readonly" class="node-toolbar"> | ||||||
|  |           <div class="toolbar-icon"> | ||||||
|  |             <IconifyIcon | ||||||
|  |               color="#0089ff" | ||||||
|  |               icon="ep:circle-close-filled" | ||||||
|  |               :size="18" | ||||||
|  |               @click="deleteNode" | ||||||
|  |             /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <!-- 传递子节点给添加节点组件。会在子节点前面添加节点 --> | ||||||
|  |       <NodeHandler | ||||||
|  |         v-if="currentNode" | ||||||
|  |         v-model:child-node="currentNode.childNode" | ||||||
|  |         :current-node="currentNode" | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |     <CopyTaskNodeConfig | ||||||
|  |       v-if="!readonly && currentNode" | ||||||
|  |       ref="nodeSetting" | ||||||
|  |       :flow-node="currentNode" | ||||||
|  |     /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <style lang="scss" scoped></style> | ||||||
|  | @ -3,6 +3,7 @@ import type { SimpleFlowNode } from '../consts'; | ||||||
| 
 | 
 | ||||||
| import { NodeType } from '../consts'; | import { NodeType } from '../consts'; | ||||||
| import { useWatchNode } from '../helpers'; | import { useWatchNode } from '../helpers'; | ||||||
|  | import CopyTaskNode from './nodes/copy-task-node.vue'; | ||||||
| import EndEventNode from './nodes/end-event-node.vue'; | import EndEventNode from './nodes/end-event-node.vue'; | ||||||
| import StartUserNode from './nodes/start-user-node.vue'; | import StartUserNode from './nodes/start-user-node.vue'; | ||||||
| import UserTaskNode from './nodes/user-task-node.vue'; | import UserTaskNode from './nodes/user-task-node.vue'; | ||||||
|  | @ -77,11 +78,11 @@ const recursiveFindParentNode = ( | ||||||
|     @find-parent-node="findParentNode" |     @find-parent-node="findParentNode" | ||||||
|   /> |   /> | ||||||
|   <!-- 抄送节点 --> |   <!-- 抄送节点 --> | ||||||
|   <!-- <CopyTaskNode |   <CopyTaskNode | ||||||
|     v-if="currentNode && currentNode.type === NodeType.COPY_TASK_NODE" |     v-if="currentNode && currentNode.type === NodeType.COPY_TASK_NODE" | ||||||
|     :flow-node="currentNode" |     :flow-node="currentNode" | ||||||
|     @update:flow-node="handleModelValueUpdate" |     @update:flow-node="handleModelValueUpdate" | ||||||
|   /> --> |   /> | ||||||
|   <!-- 条件节点 --> |   <!-- 条件节点 --> | ||||||
|   <!-- <ExclusiveNode |   <!-- <ExclusiveNode | ||||||
|     v-if="currentNode && currentNode.type === NodeType.CONDITION_BRANCH_NODE" |     v-if="currentNode && currentNode.type === NodeType.CONDITION_BRANCH_NODE" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 jason
						jason