Browse Source

BPM:增加「发起人自选」的任务审批人的分配策略

YunaiV 1 year ago
parent
commit
728cf15c45

+ 3 - 2
src/api/bpm/definition/index.ts

@@ -1,8 +1,9 @@
 import request from '@/config/axios'
 
-export const getProcessDefinitionBpmnXML = async (id: number) => {
+export const getProcessDefinition = async (id: number, key: string) => {
   return await request.get({
-    url: '/bpm/process-definition/get-bpmn-xml?id=' + id
+    url: '/bpm/process-definition/get',
+    params: { id, key }
   })
 }
 

+ 1 - 5
src/components/bpmnProcessDesigner/package/penal/task/task-components/UserTask.vue

@@ -65,11 +65,7 @@
       </el-select>
     </el-form-item>
     <el-form-item
-      v-if="
-        userTaskForm.candidateStrategy == 30 ||
-        userTaskForm.candidateStrategy == 31 ||
-        userTaskForm.candidateStrategy == 32
-      "
+      v-if="userTaskForm.candidateStrategy == 30"
       label="指定用户"
       prop="candidateParam"
       span="24"

+ 4 - 4
src/views/bpm/definition/index.vue

@@ -72,8 +72,8 @@
   <Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
     <MyProcessViewer
       key="designer"
-      v-model="bpmnXML"
-      :value="bpmnXML as any"
+      v-model="bpmnXml"
+      :value="bpmnXml as any"
       v-bind="bpmnControlForm"
       :prefix="bpmnControlForm.prefix"
     />
@@ -133,12 +133,12 @@ const handleFormDetail = async (row) => {
 
 /** 流程图的详情按钮操作 */
 const bpmnDetailVisible = ref(false)
-const bpmnXML = ref(null)
+const bpmnXml = ref(null)
 const bpmnControlForm = ref({
   prefix: 'flowable'
 })
 const handleBpmnDetail = async (row) => {
-  bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(row.id)
+  bpmnXml.value = (await DefinitionApi.getProcessDefinition(row.id))?.bpmnXml
   bpmnDetailVisible.value = true
 }
 

+ 79 - 3
src/views/bpm/oa/leave/create.vue

@@ -37,6 +37,36 @@
     <el-form-item label="原因" prop="reason">
       <el-input v-model="formData.reason" placeholder="请输请假原因" type="textarea" />
     </el-form-item>
+    <el-col v-if="startUserSelectTasks.length > 0">
+      <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>
     <el-form-item>
       <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
     </el-form-item>
@@ -46,10 +76,15 @@
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 import * as LeaveApi from '@/api/bpm/leave'
 import { useTagsViewStore } from '@/store/modules/tagsView'
+import * as DefinitionApi from '@/api/bpm/definition'
+import * as UserApi from '@/api/system/user'
 
 defineOptions({ name: 'BpmOALeaveCreate' })
 
 const message = useMessage() // 消息弹窗
+const { delView } = useTagsViewStore() // 视图操作
+const { push, currentRoute } = useRouter() // 路由
+
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
 const formData = ref({
   type: undefined,
@@ -64,18 +99,34 @@ const formRules = reactive({
   endTime: [{ required: true, message: '请假结束时间不能为空', trigger: 'change' }]
 })
 const formRef = ref() // 表单 Ref
-const { delView } = useTagsViewStore() // 视图操作
-const { push, currentRoute } = useRouter() // 路由
+
+// 指定审批人
+const processDefineKey = 'oa_leave' // 流程定义 Key
+const startUserSelectTasks = ref([]) // 发起人需要选择审批人的用户任务列表
+const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
+const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
+const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
+const userList = ref<any[]>([]) // 用户列表
+
 /** 提交表单 */
 const submitForm = async () => {
   // 校验表单
   if (!formRef) return
   const valid = await formRef.value.validate()
   if (!valid) return
+  // 校验指定审批人
+  if (startUserSelectTasks.value?.length > 0) {
+    await startUserSelectAssigneesFormRef.value.validate()
+  }
+
   // 提交请求
   formLoading.value = true
   try {
-    const data = formData.value as unknown as LeaveApi.LeaveVO
+    const data = { ...formData.value } as unknown as LeaveApi.LeaveVO
+    // 设置指定审批人
+    if (startUserSelectTasks.value?.length > 0) {
+      data.startUserSelectAssignees = startUserSelectAssignees.value
+    }
     await LeaveApi.createLeave(data)
     message.success('发起成功')
     // 关闭当前 Tab
@@ -85,4 +136,29 @@ const submitForm = async () => {
     formLoading.value = false
   }
 }
+
+/** 初始化 */
+onMounted(async () => {
+  const processDefinitionDetail = await DefinitionApi.getProcessDefinition(
+    undefined,
+    processDefineKey
+  )
+  if (!processDefinitionDetail) {
+    message.error('OA 请假的流程模型未配置,请检查!')
+    return
+  }
+  startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
+  // 设置指定审批人
+  if (startUserSelectTasks.value?.length > 0) {
+    // 设置校验规则
+    for (const userTask of startUserSelectTasks.value) {
+      startUserSelectAssignees.value[userTask.id] = []
+      startUserSelectAssigneesFormRules.value[userTask.id] = [
+        { required: true, message: '请选择审批人', trigger: 'blur' }
+      ]
+    }
+    // 加载用户列表
+    userList.value = await UserApi.getSimpleUserList()
+  }
+})
 </script>

+ 80 - 5
src/views/bpm/processInstance/create/index.vue

@@ -54,7 +54,40 @@
           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-card>
     <!-- 流程图预览 -->
@@ -69,6 +102,7 @@ import type { ApiAttrs } from '@form-create/element-ui/types/config'
 import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue'
 import { CategoryApi } from '@/api/bpm/category'
 import { useTagsViewStore } from '@/store/modules/tagsView'
+import * as UserApi from '@/api/system/user'
 
 defineOptions({ name: 'BpmProcessInstanceCreate' })
 
@@ -124,7 +158,6 @@ const categoryProcessDefinitionList = computed(() => {
 })
 
 // ========== 表单相关 ==========
-const bpmnXML = ref(null) // BPMN 数据
 const fApi = ref<ApiAttrs>()
 const detailForm = ref({
   rule: [],
@@ -133,17 +166,53 @@ const detailForm = ref({
 }) // 流程表单详情
 const selectProcessDefinition = ref() // 选择的流程定义
 
+// 指定审批人
+const bpmnXML = ref(null) // BPMN 数据
+const startUserSelectTasks = ref([]) // 发起人需要选择审批人的用户任务列表
+const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
+const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
+const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
+const userList = ref<any[]>([]) // 用户列表
+
 /** 处理选择流程的按钮操作 **/
 const handleSelect = async (row, formVariables) => {
   // 设置选择的流程
   selectProcessDefinition.value = row
 
+  // 重置指定审批人
+  startUserSelectTasks.value = []
+  startUserSelectAssignees.value = {}
+  startUserSelectAssigneesFormRules.value = {}
+
   // 情况一:流程表单
   if (row.formType == 10) {
     // 设置表单
     setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables)
     // 加载流程图
-    bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(row.id)
+    const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
+    if (processDefinitionDetail) {
+      bpmnXML.value = processDefinitionDetail.bpmnXml
+      startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
+
+      // 设置指定审批人
+      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()
+      }
+    }
     // 情况二:业务表单
   } else if (row.formCustomCreatePath) {
     await push({
@@ -158,19 +227,25 @@ const submitForm = async (formData) => {
   if (!fApi.value || !selectProcessDefinition.value) {
     return
   }
+  // 如果有指定审批人,需要校验
+  if (startUserSelectTasks.value?.length > 0) {
+    await startUserSelectAssigneesFormRef.value.validate()
+  }
+
   // 提交请求
   fApi.value.btn.loading(true)
   try {
     await ProcessInstanceApi.createProcessInstance({
       processDefinitionId: selectProcessDefinition.value.id,
-      variables: formData
+      variables: formData,
+      startUserSelectAssignees: startUserSelectAssignees.value
     })
     // 提示
     message.success('发起流程成功')
     // 跳转回去
     delView(unref(currentRoute))
     await push({
-      name: 'BpmProcessInstance'
+      name: 'BpmProcessInstanceMy'
     })
   } finally {
     fApi.value.btn.loading(false)

+ 10 - 7
src/views/bpm/processInstance/detail/ProcessInstanceBpmnViewer.vue

@@ -34,14 +34,17 @@ const bpmnControlForm = ref({
 })
 const activityList = ref([]) // 任务列表
 
-/** 初始化 */
-onMounted(async () => {
-  if (props.id) {
-    activityList.value = await ActivityApi.getActivityList({
-      processInstanceId: props.id
-    })
+/** 只有 loading 完成时,才去加载流程列表 */
+watch(
+  () => props.loading,
+  async (value) => {
+    if (value && props.id) {
+      activityList.value = await ActivityApi.getActivityList({
+        processInstanceId: props.id
+      })
+    }
   }
-})
+)
 </script>
 <style>
 .box-card {

+ 5 - 3
src/views/bpm/processInstance/detail/index.vue

@@ -115,7 +115,7 @@
     <!-- 高亮流程图 -->
     <ProcessInstanceBpmnViewer
       :id="`${id}`"
-      :bpmn-xml="bpmnXML"
+      :bpmn-xml="bpmnXml"
       :loading="processInstanceLoading"
       :process-instance="processInstance"
       :tasks="tasks"
@@ -158,7 +158,7 @@ const userId = useUserStore().getUser.id // 当前登录的编号
 const id = query.id as unknown as string // 流程实例的编号
 const processInstanceLoading = ref(false) // 流程实例的加载中
 const processInstance = ref<any>({}) // 流程实例
-const bpmnXML = ref('') // BPMN XML
+const bpmnXml = ref('') // BPMN XML
 const tasksLoad = ref(true) // 任务的加载中
 const tasks = ref<any[]>([]) // 任务列表
 // ========== 审批信息 ==========
@@ -290,7 +290,9 @@ const getProcessInstance = async () => {
     }
 
     // 加载流程图
-    bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(processDefinition.id as number)
+    bpmnXml.value = (
+      await DefinitionApi.getProcessDefinition(processDefinition.id as number)
+    )?.bpmnXml
   } finally {
     processInstanceLoading.value = false
   }