Parcourir la source

feat: [bpm] 退回任务

jason il y a 3 mois
Parent
commit
a7d3d42111

+ 10 - 0
src/api/bpm/task/index.ts

@@ -71,3 +71,13 @@ export function delegateTask(data: { id: string, delegateUserId: string, reason:
 export function transferTask(data: { id: string, assigneeUserId: string, reason: string }) {
   return http.put<boolean>('/bpm/task/transfer', data)
 }
+
+/** 退回任务 */
+export function returnTask(data: { id: string, targetTaskDefinitionKey: string, reason: string }) {
+  return http.put<boolean>('/bpm/task/return', data)
+}
+
+/** 获取可退回的节点列表 */
+export function getTaskListByReturn(taskId: string) {
+  return http.get<any[]>(`/bpm/task/list-by-return?id=${taskId}`)
+}

+ 35 - 33
src/pages-bpm/processInstance/detail/components/operation-button.vue

@@ -1,40 +1,39 @@
 <!-- 操作按钮 -->
 <template>
-  <view class="yd-detail-footer">
-    <!-- 有待审批的任务 -->
-    <view v-if="runningTask">
-      <view v-if="leftOperations.length > 0" class="w-full flex items-center">
-        <!-- 左侧操作按钮 -->
-        <view v-for="(action, idx) in leftOperations" :key="idx" class="mr-32rpx w-60rpx flex flex-col items-center" @click="handleOperation(action.operationType)">
-          <wd-icon :name="action.iconName" size="40rpx" color="#1890ff" />
-          <text class="mt-4rpx text-22rpx text-[#333]">{{ action.displayName }}</text>
-        </view>
-        <!-- 更多操作按钮 -->
-        <view v-if="moreOperations.length > 0" class="mr-32rpx w-60rpx flex flex-col items-center" @click="handleShowMore">
-          <wd-icon name="ellipsis" size="40rpx" color="#1890ff" />
-          <text class="mt-4rpx text-22rpx text-[#333]">更多</text>
-        </view>
-        <!-- 右侧按钮,TODO 是否一定要保留两个按钮 -->
-        <view class="flex flex-1 gap-16rpx">
-          <wd-button
-            v-for="(action, idx) in rightOperations"
-            :key="idx"
-            :plain="action.plain"
-            :type="action.btnType"
-            :round="false"
-            class="flex-1"
-            custom-style="min-width: 200rpx; width: 200rpx;"
-            @click="handleOperation(action.operationType)"
-          >
-            {{ action.displayName }}
-          </wd-button>
-        </view>
+  <!-- 有待审批的任务 -->
+  <view v-if="runningTask" class="yd-detail-footer">
+    <view class="w-full flex items-center">
+      <!-- 左侧操作按钮 -->
+      <view v-for="(action, idx) in leftOperations" :key="idx" class="mr-32rpx w-60rpx flex flex-col items-center" @click="handleOperation(action.operationType)">
+        <wd-icon :name="action.iconName" size="40rpx" color="#1890ff" />
+        <text class="mt-4rpx text-22rpx text-[#333]">{{ action.displayName }}</text>
+      </view>
+      <!-- 更多操作按钮 -->
+      <view v-if="moreOperations.length > 0" class="mr-32rpx w-60rpx flex flex-col items-center" @click="handleShowMore">
+        <wd-icon name="ellipsis" size="40rpx" color="#1890ff" />
+        <text class="mt-4rpx text-22rpx text-[#333]">更多</text>
       </view>
       <!-- 更多操作 ActionSheet -->
       <wd-action-sheet v-if="moreOperations.length > 0" v-model="showMoreActions" :actions="moreOperations" title="请选择操作" @select="handleMoreAction" />
+
+      <!-- 右侧按钮,TODO 是否一定要保留两个按钮 -->
+      <view class="flex flex-1 gap-16rpx">
+        <wd-button
+          v-for="(action, idx) in rightOperations"
+          :key="idx"
+          :plain="action.plain"
+          :type="action.btnType"
+          :round="false"
+          class="flex-1"
+          custom-style="min-width: 200rpx; width: 200rpx;"
+          @click="handleOperation(action.operationType)"
+        >
+          {{ action.displayName }}
+        </wd-button>
+      </view>
     </view>
-    <!-- TODO 无待审批的任务 需要显示什么 -->
   </view>
+  <!-- TODO 无待审批的任务 需要显示什么 -->
 </template>
 
 <script lang="ts" setup>
@@ -91,7 +90,7 @@ function loadTodoTask(task: Task) {
   if (task) {
     reasonRequire.value = task.reasonRequire ?? false
     // 右侧按钮
-    if (isHandleTaskStatus() && task.buttonsSetting[BpmTaskOperationButtonTypeEnum.REJECT]?.enable) {
+    if (isHandleTaskStatus() && task.buttonsSetting && task.buttonsSetting[BpmTaskOperationButtonTypeEnum.REJECT]?.enable) {
       rightOperationTypes.push(BpmTaskOperationButtonTypeEnum.REJECT)
       rightOperations.value.push({
         operationType: BpmTaskOperationButtonTypeEnum.REJECT,
@@ -100,7 +99,7 @@ function loadTodoTask(task: Task) {
         plain: true,
       })
     }
-    if (isHandleTaskStatus() && task.buttonsSetting[BpmTaskOperationButtonTypeEnum.APPROVE]?.enable) {
+    if (isHandleTaskStatus() && task.buttonsSetting && task.buttonsSetting[BpmTaskOperationButtonTypeEnum.APPROVE]?.enable) {
       rightOperationTypes.push(BpmTaskOperationButtonTypeEnum.APPROVE)
       rightOperations.value.push({
         operationType: BpmTaskOperationButtonTypeEnum.APPROVE,
@@ -109,6 +108,7 @@ function loadTodoTask(task: Task) {
         plain: false,
       })
     }
+    console.log('rightOperationTypes====>', rightOperationTypes)
     // TODO 减签
     // 左侧操作,和更多操作
     Object.keys(task.buttonsSetting || {}).forEach((key) => {
@@ -157,7 +157,9 @@ function handleOperation(operationType: number) {
       toast.show('加签功能待实现')
       break
     case BpmTaskOperationButtonTypeEnum.RETURN:
-      toast.show('退回功能待实现')
+      uni.navigateTo({
+        url: `/pages-bpm/processInstance/detail/return/index?processInstanceId=${runningTask.value.processInstanceId}&taskId=${runningTask.value.id}`,
+      })
       break
   }
 }

+ 157 - 0
src/pages-bpm/processInstance/detail/return/index.vue

@@ -0,0 +1,157 @@
+<template>
+  <view class="yd-page-container">
+    <!-- 顶部导航栏 -->
+    <wd-navbar
+      title="退回任务"
+      left-arrow placeholder safe-area-inset-top fixed
+      @click-left="handleBack"
+    />
+
+    <!-- 操作表单 -->
+    <view class="p-24rpx">
+      <wd-form ref="formRef" :model="formData" :rules="formRules">
+        <wd-cell-group border>
+          <!-- 退回节点选择 -->
+          <wd-picker
+            v-model="formData.targetActivityId"
+            label="退回节点:"
+            prop="targetActivityId"
+            :columns="activityOptions"
+            value-key="taskDefinitionKey"
+            label-key="name"
+            placeholder="请选择退回节点"
+          />
+
+          <!-- 退回原因 -->
+          <wd-textarea
+            v-model="formData.reason"
+            prop="reason"
+            label="退回原因:"
+            label-width="180rpx"
+            placeholder="请输入退回原因"
+            :maxlength="500"
+            show-word-limit
+            clearable
+          />
+        </wd-cell-group>
+        <!-- 提交按钮 -->
+        <view class="mt-48rpx">
+          <wd-button
+            type="primary"
+            block
+            :loading="submitting"
+            :disabled="submitting"
+            @click="handleSubmit"
+          >
+            退回
+          </wd-button>
+        </view>
+      </wd-form>
+    </view>
+  </view>
+</template>
+
+<script lang="ts" setup>
+import type { FormInstance } from 'wot-design-uni/components/wd-form/types'
+import { computed, onMounted, reactive, ref } from 'vue'
+import { useToast } from 'wot-design-uni'
+import { getTaskListByReturn, returnTask } from '@/api/bpm/task'
+import { navigateBackPlus } from '@/utils'
+
+const props = defineProps<{
+  processInstanceId: string
+  taskId: string
+}>()
+
+definePage({
+  style: {
+    navigationBarTitleText: '',
+    navigationStyle: 'custom',
+  },
+})
+
+const taskId = computed(() => props.taskId)
+const processInstanceId = computed(() => props.processInstanceId)
+const toast = useToast()
+const submitting = ref(false)
+const formRef = ref<FormInstance>()
+const activityOptions = ref<any[]>([])
+
+const formData = reactive({
+  targetActivityId: '',
+  reason: '',
+})
+
+const formRules = {
+  targetActivityId: [
+    { required: true, message: '退回节点不能为空' },
+  ],
+  reason: [
+    { required: true, message: '退回原因不能为空' },
+  ],
+}
+
+/** 返回上一页 */
+function handleBack() {
+  navigateBackPlus(`/pages-bpm/processInstance/detail/index?id=${processInstanceId.value}&taskId=${taskId.value}`)
+}
+
+/** 初始化校验 */
+if (!props.taskId || !props.processInstanceId) {
+  toast.show('参数错误')
+}
+
+/** 获取可退回的节点列表 */
+async function loadReturnTaskList() {
+  try {
+    const result = await getTaskListByReturn(taskId.value)
+    console.log('[return] 获取可退回节点:', result)
+    if (result && Array.isArray(result)) {
+      activityOptions.value = result
+    }
+  } catch (error) {
+    console.error('[return] 获取可退回节点失败:', error)
+    toast.error('获取可退回节点失败')
+  }
+}
+
+/** 提交操作 */
+async function handleSubmit() {
+  if (submitting.value)
+    return
+
+  // 使用 wd-form 的校验方法
+  const { valid } = await formRef.value!.validate()
+  if (!valid) {
+    return
+  }
+
+  submitting.value = true
+  try {
+    const result = await returnTask({
+      id: taskId.value as string,
+      targetTaskDefinitionKey: formData.targetActivityId,
+      reason: formData.reason,
+    })
+
+    if (result) {
+      toast.success('退回成功')
+      setTimeout(() => {
+        uni.redirectTo({
+          url: `/pages-bpm/processInstance/detail/index?id=${processInstanceId.value}&taskId=${taskId.value}`,
+        })
+      }, 1500)
+    }
+  } catch (error) {
+    console.error('[return] 退回失败:', error)
+    toast.error('退回失败')
+  } finally {
+    submitting.value = false
+  }
+}
+
+/** 页面加载时获取可退回节点列表 */
+onMounted(() => {
+  loadReturnTaskList()
+})
+</script>