feat: [bpm] 流程取消操作

This commit is contained in:
jason
2026-01-13 14:23:57 +08:00
parent dfa374fdc9
commit a11ed15d3b
5 changed files with 206 additions and 25 deletions

View File

@@ -33,12 +33,24 @@
</view>
</view>
</view>
<!-- TODO 无待审批的任务 需要显示什么 -->
<!-- 无待审批的任务 仅显示取消按钮TODO 看看还需要显示 -->
<view v-if="!runningTask && isShowProcessStartCancel()" class="yd-detail-footer">
<wd-button
plain
type="primary"
:round="false"
block
@click="handleOperation(BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL)"
>
取消
</wd-button>
</view>
</template>
<script lang="ts" setup>
import type { Action } from 'wot-design-uni/components/wd-action-sheet/types'
import type { ButtonType } from 'wot-design-uni/components/wd-button/types'
import type { ProcessInstance } from '@/api/bpm/processInstance'
import type { Task } from '@/api/bpm/task'
import { useToast } from 'wot-design-uni'
import { useUserStore } from '@/store'
@@ -74,23 +86,26 @@ const operationIconsMap: Record<number, string> = {
[BpmTaskOperationButtonTypeEnum.RETURN]: 'arrow-left',
[BpmTaskOperationButtonTypeEnum.COPY]: 'copy',
[BpmTaskOperationButtonTypeEnum.DELETE_SIGN]: 'remove',
[BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL]: 'stop-circle',
}
const userStore = useUserStore()
/** 左侧操作按钮 【最多两个】{转办, 委派, 退回, 加签, 抄送等} */
const leftOperations = ref<LeftOperationType[]>([])
/** 右侧操作按钮【最多两个】{通过,拒绝, 取消,减签} */
/** 右侧操作按钮【最多两个】{通过,拒绝, 取消} */
const rightOperationTypes = []
const rightOperations = ref<RightOperationType[]>([])
/** 更多操作 */
const moreOperations = ref<MoreOperationType[]>([])
const toast = useToast()
const runningTask = ref<Task>()
const processInstance = ref<ProcessInstance>()
const reasonRequire = ref<boolean>(false)
/** 加载待办任务 */
function loadTodoTask(task: Task) {
/** 初始化 */
function init(theProcessInstance: ProcessInstance, task: Task) {
processInstance.value = theProcessInstance
runningTask.value = task
if (task) {
reasonRequire.value = task.reasonRequire ?? false
@@ -149,12 +164,35 @@ function loadTodoTask(task: Task) {
}
}
}
// 是否显示流程取消
if (isShowProcessStartCancel()) {
if (rightOperationTypes.length < 2) {
rightOperationTypes.push(BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL)
rightOperations.value.push({
operationType: BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL,
displayName: getButtonDisplayName(BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL),
btnType: 'primary',
plain: true,
})
} else {
if (leftOperations.value.length >= 2) {
moreOperations.value.push({
name: getButtonDisplayName(BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL),
operationType: BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL,
})
} else {
leftOperations.value.push({
operationType: BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL,
iconName: operationIconsMap[BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL],
displayName: getButtonDisplayName(BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL),
})
}
}
}
}
/** 跳转到相应的操作页面 */
function handleOperation(operationType: number) {
if (!runningTask.value) {
return
}
switch (operationType) {
case BpmTaskOperationButtonTypeEnum.APPROVE:
uni.navigateTo({ url: `/pages-bpm/processInstance/detail/audit/index?id=${runningTask.value.id}&pass=true` })
@@ -187,6 +225,11 @@ function handleOperation(operationType: number) {
url: `/pages-bpm/processInstance/detail/delete-sign/index?processInstanceId=${runningTask.value.processInstanceId}&taskId=${runningTask.value.id}&children=${encodeURIComponent(JSON.stringify(runningTask.value.children || []))}`,
})
break
case BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL:
uni.navigateTo({
url: `/pages-bpm/processInstance/detail/process-cancel/index?processInstanceId=${processInstance.value.id}&taskId=${runningTask.value?.id}`,
})
break
}
}
@@ -250,7 +293,7 @@ function isEndProcessStatus(status: number) {
/** 流程发起人是否为当前用户 */
function isProcessStartUser() {
let isStartUser = false
if (userStore.userInfo?.userId === runningTask.value?.processInstance?.startUser?.id) {
if (userStore.userInfo?.userId === processInstance.value?.startUser?.id) {
isStartUser = true
}
return isStartUser
@@ -263,9 +306,9 @@ function isShowDeleteSign() {
/** 是否显示流程发起人取消 */
function isShowProcessStartCancel() {
return isProcessStartUser
return isProcessStartUser() && !isEndProcessStatus(processInstance.value?.status)
}
/** 暴露方法 */
defineExpose({ loadTodoTask })
defineExpose({ init })
</script>

View File

@@ -12,32 +12,31 @@
<view class="p-24rpx">
<!-- 标题和状态 -->
<view class="mb-16rpx flex items-center justify-between">
<text class="text-32rpx text-[#333] font-bold">{{ processInstance.name }}</text>
<wd-tag :type="getStatusType(processInstance.status)">
{{ getStatusText(processInstance.status) }}
<text class="text-32rpx text-[#333] font-bold">{{ processInstance?.name }}</text>
<wd-tag :type="getStatusType(processInstance?.status)">
{{ getStatusText(processInstance?.status) }}
</wd-tag>
</view>
<!-- 发起人信息 -->
<view class="flex items-center">
<view class="mr-12rpx h-64rpx w-64rpx flex items-center justify-center rounded-full bg-[#1890ff] text-white">
{{ processInstance.startUser?.nickname?.[0] || '?' }}
{{ processInstance?.startUser?.nickname?.[0] || '?' }}
</view>
<view>
<text class="text-28rpx text-[#333]">{{ processInstance.startUser?.nickname }}</text>
<text v-if="processInstance.startUser?.deptName" class="ml-8rpx text-24rpx text-[#999]">
{{ processInstance.startUser?.deptName }}
<text class="text-28rpx text-[#333]">{{ processInstance?.startUser?.nickname }}</text>
<text v-if="processInstance?.startUser?.deptName" class="ml-8rpx text-24rpx text-[#999]">
{{ processInstance?.startUser?.deptName }}
</text>
</view>
</view>
<!-- 提交时间 -->
<view class="mt-16rpx text-24rpx text-[#999]">
提交于 {{ formatDateTime(processInstance.startTime) }}
提交于 {{ formatDateTime(processInstance?.startTime) }}
</view>
</view>
</view>
<!-- 区域审批详情表单 -->
<!-- TODO @jason看看 idea 告警怎么优化下 -->
<FormDetail :process-definition="processDefinition" :process-instance="processInstance" />
<!-- 区域审批记录 TODO @jason抽成类似 /Users/yunai/Java/yudao-ui-admin-vben-v5/apps/web-antd/src/views/bpm/processInstance/detail/modules/task-list.vue -->
@@ -89,7 +88,7 @@
<!-- TODO 待开发区域流程评论 -->
<!-- 区域底部操作栏 -->
<ProcessInstanceOperationButton ref="operationButtonRef" />
<ProcessInstanceOperationButton ref="operationButtonRef" :process-instance="processInstance" />
</view>
</template>
@@ -120,8 +119,8 @@ definePage({
const userStore = useUserStore()
const toast = useToast()
const processInstance = ref<Partial<ProcessInstance>>({})
const processDefinition = ref<Partial<ProcessDefinition>>({})
const processInstance = ref<ProcessInstance>()
const processDefinition = ref<ProcessDefinition>()
const tasks = ref<Task[]>([])
const orderAsc = ref(true)
@@ -238,8 +237,8 @@ async function loadProcessInstance() {
return
}
processInstance.value = data.processInstance
processDefinition.value = data.processDefinition || {}
operationButtonRef.value?.loadTodoTask(data.todoTask)
processDefinition.value = data.processDefinition
operationButtonRef.value?.init(data.processInstance, data.todoTask)
}
/** 加载任务列表 */

View File

@@ -0,0 +1,133 @@
<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>
<!-- 友情提醒 -->
<view class="mb-24rpx border border-[#ffd591] rounded-16rpx bg-[#fff7e6] p-24rpx">
<view class="mb-12rpx flex items-center">
<wd-icon name="warning" color="#faad14" size="32rpx" />
<text class="ml-12rpx text-28rpx text-[#faad14] font-bold">友情提醒</text>
</view>
<text class="text-26rpx text-[#666]">取消后该审批流程将自动结束</text>
</view>
<!-- 取消理由 -->
<wd-textarea
v-model="formData.cancelReason"
prop="cancelReason"
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, reactive, ref } from 'vue'
import { useToast } from 'wot-design-uni'
import { cancelProcessInstanceByStartUser } from '@/api/bpm/processInstance'
import { navigateBackPlus } from '@/utils'
const props = defineProps<{
processInstanceId: string
taskId?: string
}>()
definePage({
style: {
navigationBarTitleText: '',
navigationStyle: 'custom',
},
})
const processInstanceId = computed(() => props.processInstanceId)
const taskId = computed(() => props.taskId)
const toast = useToast()
const submitting = ref(false)
const formRef = ref<FormInstance>()
const formData = reactive({
cancelReason: '',
})
const formRules = {
cancelReason: [
{ required: true, message: '取消理由不能为空' },
],
}
/** 返回上一页 */
function handleBack() {
const backUrl = taskId.value
? `/pages-bpm/processInstance/detail/index?id=${processInstanceId.value}&taskId=${taskId.value}`
: `/pages-bpm/processInstance/detail/index?id=${processInstanceId.value}`
navigateBackPlus(backUrl)
}
/** 初始化校验 */
if (!props.processInstanceId) {
toast.show('参数错误')
}
/** 提交操作 */
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 cancelProcessInstanceByStartUser(
processInstanceId.value,
formData.cancelReason,
)
if (result) {
toast.success('流程取消成功')
setTimeout(() => {
uni.redirectTo({
url: `/pages-bpm/processInstance/detail/index?id=${processInstanceId.value}`,
})
}, 1500)
}
} catch (error) {
console.error('[process-cancel] 取消流程失败:', error)
toast.error('取消流程失败')
} finally {
submitting.value = false
}
}
</script>

View File

@@ -63,6 +63,8 @@ export const useUserStore = defineStore(
/** 获取用户信息 */
const fetchUserInfo = async () => {
const res = await getAuthPermissionInfo()
// 后端返回的用户 Id 字段为 id.
res.user.userId = res.user.id
setUserInfo(res)
return res
}

View File

@@ -151,6 +151,10 @@ export enum BpmTaskOperationButtonTypeEnum {
* 减签
*/
DELETE_SIGN = 8,
/**
* 流程发起人取消
*/
PROCESS_START_CANCEL = 9,
/**
* 拒绝
*/
@@ -163,7 +167,6 @@ export enum BpmTaskOperationButtonTypeEnum {
* 转办
*/
TRANSFER = 3,
}
/**
@@ -292,6 +295,7 @@ OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.ADD_SIGN, '加签')
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.RETURN, '退回')
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.COPY, '抄送')
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.DELETE_SIGN, '减签')
OPERATION_BUTTON_NAME.set(BpmTaskOperationButtonTypeEnum.PROCESS_START_CANCEL, '取消')
/**
* 流程实例的变量枚举