feat: [bpm] 流程取消操作
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/** 加载任务列表 */
|
||||
|
||||
133
src/pages-bpm/processInstance/detail/process-cancel/index.vue
Normal file
133
src/pages-bpm/processInstance/detail/process-cancel/index.vue
Normal 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>
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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, '取消')
|
||||
|
||||
/**
|
||||
* 流程实例的变量枚举
|
||||
|
||||
Reference in New Issue
Block a user