Bladeren bron

feat: 工作流发起页面-需要自定义选择审批人的节点改为在右侧timeline组件中跳出弹窗选择

GoldenZqqq 9 maanden geleden
bovenliggende
commit
8b5740d05c

+ 16 - 8
src/components/UserSelectForm/index.vue

@@ -15,7 +15,7 @@
       </el-col>
       <el-col :span="17" :offset="1">
         <el-transfer
-          v-model="selectedUserList"
+          v-model="selectedUserIdList"
           filterable
           filter-placeholder="搜索成员"
           :data="userList"
@@ -24,7 +24,12 @@
       </el-col>
     </el-row>
     <template #footer>
-      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button
+        :disabled="formLoading || !selectedUserIdList?.length"
+        type="primary"
+        @click="submitForm"
+        >确 定</el-button
+      >
       <el-button @click="dialogVisible = false">取 消</el-button>
     </template>
   </Dialog>
@@ -36,21 +41,24 @@ import * as UserApi from '@/api/system/user'
 
 defineOptions({ name: 'UserSelectForm' })
 const emit = defineEmits<{
-  confirm: [userList: any[]]
+  confirm: [id: any, userList: any[]]
 }>()
 const { t } = useI18n() // 国际
 const deptList = ref<Tree[]>([]) // 部门树形结构化
 const userList: any = ref([]) // 用户列表
 const message = useMessage() // 消息弹窗
-const selectedUserList: any = ref([]) // 选中的用户列表
+const selectedUserIdList: any = ref([]) // 选中的用户列表
 const dialogVisible = ref(false) // 弹窗的是否展示
 const formLoading = ref(false) // 表单的加载中
+const activityId = ref() // 主键id
 
 /** 打开弹窗 */
-const open = async () => {
+const open = async (id, selectedList?) => {
+  activityId.value = id
   resetForm()
   deptList.value = handleTree(await DeptApi.getSimpleDeptList())
   await getUserList()
+  selectedUserIdList.value = selectedList?.map((item) => item.id)
   // 修改时,设置数据
   dialogVisible.value = true
 }
@@ -71,8 +79,9 @@ const submitForm = async () => {
   try {
     message.success(t('common.updateSuccess'))
     dialogVisible.value = false
+    const emitUserList = userList.value.filter((user) => selectedUserIdList.value.includes(user.id))
     // 发送操作成功的事件
-    emit('confirm', selectedUserList.value)
+    emit('confirm', activityId.value, emitUserList)
   } finally {
     formLoading.value = false
   }
@@ -80,13 +89,12 @@ const submitForm = async () => {
 const resetForm = () => {
   deptList.value = []
   userList.value = []
-  selectedUserList.value = []
+  selectedUserIdList.value = []
 }
 
 /** 处理部门被点击 */
 const handleNodeClick = async (row: { [key: string]: any }) => {
   getUserList(row.id)
 }
-
 defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 </script>

+ 21 - 61
src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue

@@ -19,40 +19,7 @@
                       v-model="detailForm.value"
                       :option="detailForm.option"
                       @submit="submitForm"
-                    >
-                      <!-- <template #type-startUserSelect>
-                        <el-col :span="24">
-                          <el-card class="mb-10px">
-                            <template #header>指定审批人</template>
-                            <el-form
-                              :model="startUserSelectAssignees"
-                              :rules="startUserSelectAssigneesFormRules"
-                              ref="startUserSelectAssigneesFormRef"
-                            >
-                              <el-form-item
-                                v-for="userTask in startUserSelectTasks"
-                                :key="userTask.id"
-                                :label="`任务【${userTask.name}】`"
-                                :prop="userTask.id"
-                              >
-                                <el-select
-                                  v-model="startUserSelectAssignees[userTask.id]"
-                                  multiple
-                                  placeholder="请选择审批人"
-                                >
-                                  <el-option
-                                    v-for="user in userList"
-                                    :key="user.id"
-                                    :label="user.nickname"
-                                    :value="user.id"
-                                  />
-                                </el-select>
-                              </el-form-item>
-                            </el-form>
-                          </el-card>
-                        </el-col>
-                      </template> -->
-                    </form-create>
+                    />
                   </el-col>
 
                   <el-col :span="6" :offset="1">
@@ -62,6 +29,8 @@
                       :activity-nodes="activityNodes"
                       :show-status-icon="false"
                       :startUserSelectTasks="startUserSelectTasks"
+                      :startUserSelectAssignees="startUserSelectAssignees"
+                      @select-user-confirm="selectUserConfirm"
                     />
                   </el-col>
                 </el-row>
@@ -104,8 +73,7 @@ import type { ApiAttrs } from '@form-create/element-ui/types/config'
 import { useTagsViewStore } from '@/store/modules/tagsView'
 import * as ProcessInstanceApi from '@/api/bpm/processInstance'
 import * as DefinitionApi from '@/api/bpm/definition'
-// import * as UserApi from '@/api/system/user'
-import { activityNodes as aN, startUserSelectTasks as sUs } from './mock'
+// import { activityNodes as aN, startUserSelectTasks as sUs } from './mock'
 
 defineOptions({ name: 'ProcessDefinitionDetail' })
 const props = defineProps<{
@@ -122,11 +90,8 @@ const detailForm: any = ref({
 }) // 流程表单详情
 const fApi = ref<ApiAttrs>()
 // 指定审批人
-const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
 const startUserSelectTasks: any = ref([]) // 发起人需要选择审批人的用户任务列表
 const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
-const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
-// const userList = ref<any[]>([]) // 用户列表
 const bpmnXML: any = ref(null) // BPMN 数据
 /** 当前的Tab */
 const activeTab = ref('form')
@@ -139,7 +104,6 @@ const initProcessInfo = async (row: any, formVariables?: any) => {
   // 重置指定审批人
   startUserSelectTasks.value = []
   startUserSelectAssignees.value = {}
-  startUserSelectAssigneesFormRules.value = {}
 
   // 情况一:流程表单
   if (row.formType == 10) {
@@ -164,25 +128,13 @@ const initProcessInfo = async (row: any, formVariables?: any) => {
     if (processDefinitionDetail) {
       bpmnXML.value = processDefinitionDetail.bpmnXml
       startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
-      startUserSelectTasks.value = sUs
       // 设置指定审批人
-      // if (startUserSelectTasks.value?.length > 0) {
-      //   detailForm.value.rule.push({
-      //     type: 'startUserSelect',
-      //     props: {
-      //       title: '指定审批人'
-      //     }
-      //   })
-      //   // 设置校验规则
-      //   for (const userTask of startUserSelectTasks.value) {
-      //     startUserSelectAssignees.value[userTask.id] = []
-      //     startUserSelectAssigneesFormRules.value[userTask.id] = [
-      //       { required: true, message: '请选择审批人', trigger: 'blur' }
-      //     ]
-      //   }
-      //   // 加载用户列表
-      //   userList.value = await UserApi.getSimpleUserList()
-      // }
+      if (startUserSelectTasks.value?.length > 0) {
+        for (const userTask of startUserSelectTasks.value) {
+          // 初始化数据
+          startUserSelectAssignees.value[userTask.id] = []
+        }
+      }
     }
     // 情况二:业务表单
   } else if (row.formCustomCreatePath) {
@@ -206,19 +158,23 @@ const getApprovalDetail = async (row: any) => {
     }
     // 获取审批节点,显示 Timeline 的数据
     activityNodes.value = data.activityNodes
-    activityNodes.value = aN
   } finally {
   }
 }
 /** 提交按钮 */
 const submitForm = async (formData: any) => {
-  debugger
   if (!fApi.value || !props.selectProcessDefinition) {
     return
   }
   // 如果有指定审批人,需要校验
   if (startUserSelectTasks.value?.length > 0) {
-    await startUserSelectAssigneesFormRef.value.validate()
+    for (const userTask of startUserSelectTasks.value) {
+      if (
+        Array.isArray(startUserSelectAssignees.value[userTask.id]) &&
+        startUserSelectAssignees.value[userTask.id].length === 0
+      )
+        return message.warning(`请选择${userTask.name}的审批人`)
+    }
   }
 
   // 提交请求
@@ -245,6 +201,10 @@ const handleCancel = () => {
   emit('cancel')
 }
 
+const selectUserConfirm = (id, userList) => {
+  startUserSelectAssignees.value[id] = userList?.map((item) => item.id)
+}
+
 defineExpose({ initProcessInfo })
 </script>
 

+ 0 - 86
src/views/bpm/processInstance/create/mock.ts

@@ -1,86 +0,0 @@
-import {  ProcessInstanceVO, User, ApprovalTaskInfo, ApprovalNodeInfo } from '@/api/bpm/processInstance';
-import { NodeType } from '@/components/SimpleProcessDesignerV2/src/consts'
-
-const users: User[] = [
-  { id: 1, nickname: 'Alice', avatar: 'https://picsum.photos/200?r=1' },
-  { id: 2, nickname: 'Bob', avatar: 'https://picsum.photos/200?r=2' },
-  { id: 3, nickname: 'Charlie', avatar: 'https://picsum.photos/200?r=3' },
-  { id: 4, nickname: 'David', avatar: 'https://picsum.photos/200?r=4' }
-];
-
-const approvalTask1: ApprovalTaskInfo = {
-  id: 1,
-  ownerUser: users[0], // Alice is the owner (initiator)
-  assigneeUser: users[1], // Bob is the assignee
-  status: 1, // In Progress
-  reason: 'Please review and approve the request.'
-};
-
-const approvalTask2: ApprovalTaskInfo = {
-  id: 2,
-  ownerUser: users[1], // Bob is the owner (approver)
-  assigneeUser: users[2], // Charlie is the assignee
-  status: 0, // Pending approval
-  reason: 'Awaiting Bob’s decision.'
-};
-
-const approvalTask3: ApprovalTaskInfo = {
-  id: 3,
-  ownerUser: users[2], // Charlie is the owner (approver)
-  assigneeUser: users[3], // David is the assignee
-  status: 0, // Pending approval
-  reason: 'Awaiting Charlie’s decision.'
-};
-
-const approvalNode1: ApprovalNodeInfo = {
-  id: 101,
-  name: 'Start Review',
-  nodeType: NodeType.START_USER_NODE,
-  status: 1, // In Progress
-  startTime: new Date('2024-11-01T10:00:00Z'),
-  tasks: [approvalTask1]
-};
-
-const approvalNode2: ApprovalNodeInfo = {
-  id: 102,
-  name: 'First Review',
-  nodeType: NodeType.USER_TASK_NODE,
-  status: 0, // Pending approval
-  startTime: new Date('2024-11-01T11:00:00Z'),
-  tasks: [approvalTask2],
-  candidateUsers: [users[2], users[3]] // Candidate users: Charlie and David
-};
-
-const approvalNode3: ApprovalNodeInfo = {
-  id: 103,
-  name: 'Second Review',
-  nodeType: NodeType.USER_TASK_NODE,
-  status: 0, // Pending approval
-  startTime: new Date('2024-11-01T12:00:00Z'),
-  tasks: [approvalTask3],
-  candidateUsers: [users[1], users[3]] // Candidate users: Bob and David
-};
-
-const processInstance: ProcessInstanceVO = {
-  id: 1001,
-  name: 'Request Approval Process',
-  processDefinitionId: 'proc-2024-001',
-  category: 'Approval Process',
-  result: 0, // Ongoing
-  tasks: [{ id: '1', name: 'Start Review' }, { id: '2', name: 'First Review' }, { id: '3', name: 'Second Review' }],
-  fields: ['field1', 'field2'],
-  status: 1, // In Progress
-  remark: 'This is a sample approval process.',
-  businessKey: 'BUS-12345',
-  createTime: '2024-11-01T09:00:00Z',
-  endTime: '',
-  processDefinition: undefined // Not populated in this example
-};
-
-// 模拟的 activityNodes 数据,传递给 ProcessInstanceTimeline 组件
-const activityNodes: ApprovalNodeInfo[] = [approvalNode1, approvalNode2, approvalNode3];
-
-export { processInstance, activityNodes, users };
-
-
-export const startUserSelectTasks = users.map(user => ({id: user.id,name:user.nickname}))

+ 32 - 7
src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue

@@ -38,11 +38,29 @@
         </div>
         <!-- 需要自定义选择审批人 -->
         <div
-          v-if="startUserSelectTasks?.length > 0 && activity.nodeType === NodeType.USER_TASK_NODE"
+          class="flex flex-wrap gap2 items-center"
+          v-if="
+            startUserSelectTasks?.length > 0 && Array.isArray(startUserSelectAssignees[activity.id])
+          "
         >
-          <el-button class="!px-8px" @click="handleSelectUser">
+          <!--  && activity.nodeType === NodeType.USER_TASK_NODE -->
+          <el-button
+            class="!px-8px"
+            @click="handleSelectUser(activity.id, customApprover[activity.id])"
+          >
             <Icon icon="fa:user-plus" />
           </el-button>
+          <div
+            v-for="(user, idx1) in customApprover[activity.id]"
+            :key="idx1"
+            class="bg-gray-100 h-35px rounded-3xl flex items-center p-8px gap-2 dark:color-gray-600 position-relative"
+          >
+            <el-avatar :size="28" v-if="user.avatar" :src="user.avatar" />
+            <el-avatar :size="28" v-else>
+              {{ user.nickname.substring(0, 1) }}
+            </el-avatar>
+            {{ user.nickname }}
+          </div>
         </div>
         <div v-else class="flex items-center flex-wrap mt-1 gap2">
           <!-- 情况一:遍历每个审批节点下的【进行中】task 任务 -->
@@ -152,10 +170,12 @@ withDefaults(
     activityNodes: ProcessInstanceApi.ApprovalNodeInfo[] // 审批节点信息
     showStatusIcon?: boolean // 是否显示头像右下角状态图标
     startUserSelectTasks?: any[] // 发起人需要选择审批人的用户任务列表
+    startUserSelectAssignees?: any // 发起人选择审批人的数据
   }>(),
   {
     showStatusIcon: true, // 默认值为 true
-    startUserSelectTasks: () => [] // 默认值为空数组
+    startUserSelectTasks: () => [], // 默认值为空数组
+    startUserSelectAssignees: () => {}
   }
 )
 
@@ -256,11 +276,16 @@ const getApprovalNodeTime = (node: ProcessInstanceApi.ApprovalNodeInfo) => {
 
 // 选择自定义审批人
 const userSelectFormRef = ref()
-const handleSelectUser = () => {
-  userSelectFormRef.value.open()
+const handleSelectUser = (activityId, selectedList) => {
+  userSelectFormRef.value.open(activityId, selectedList)
 }
+const emit = defineEmits<{
+  selectUserConfirm: [id: any, userList: any[]]
+}>()
+const customApprover: any = ref({})
 // 选择完成
-const handleUserSelectConfirm = (userList) => {
-  console.log('[ userList ] >', userList)
+const handleUserSelectConfirm = (activityId, userList) => {
+  customApprover.value[activityId] = userList || []
+  emit('selectUserConfirm', activityId, userList)
 }
 </script>