feat(projects): 新增转办和终止功能

This commit is contained in:
AN 2025-06-25 23:19:32 +08:00
parent 89e7edb380
commit 80faf4b47c
5 changed files with 157 additions and 13 deletions

View File

@ -27,6 +27,12 @@ const props = withDefaults(defineProps<Props>(), {
disabledIds: () => []
});
interface Emits {
(e: 'confirm', value: CommonType.IdType[]): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
default: false
});
@ -162,6 +168,11 @@ function closeModal() {
visible.value = false;
}
function handleConfirm() {
emit('confirm', checkedRowKeys.value);
closeModal();
}
function getRowProps(row: Api.System.User) {
return {
onClick: () => {
@ -272,7 +283,7 @@ watch(visible, () => {
<template #footer>
<NSpace justify="end" :size="16">
<NButton @click="closeModal">{{ $t('common.cancel') }}</NButton>
<NButton type="primary">{{ $t('common.confirm') }}</NButton>
<NButton type="primary" @click="handleConfirm">{{ $t('common.confirm') }}</NButton>
</NSpace>
</template>
</NModal>

View File

@ -1,5 +1,7 @@
<script lang="ts" setup>
import { useBoolean, useLoading } from '~/packages/hooks/src';
import { computed, reactive, watch } from 'vue';
import { useBoolean } from '@sa/hooks';
import { fetchTaskOperate, fetchTerminateTask } from '@/service/api/workflow/task';
defineOptions({
name: 'FlowInterveneModal'
@ -10,12 +12,98 @@ const { bool: transferVisible, setTrue: openTransferModal } = useBoolean();
interface Props {
rowData: Api.Workflow.Task;
}
const { loading: btnLoading } = useLoading();
const props = defineProps<Props>();
interface Emits {
(e: 'refresh'): void;
}
const emit = defineEmits<Emits>();
const isWaiting = computed(() => props.rowData.flowStatus === 'waiting');
// 0
const isTicketOrSignInstance = computed(() => Number(props.rowData.nodeRatio) > 0);
const visible = defineModel<boolean>('visible', {
default: false
});
type Model = Api.Workflow.TaskOperateParams;
const model: Model = reactive(createDefaultModel());
function createDefaultModel(): Model {
return {
taskId: null,
userId: null,
userIds: null,
message: ''
};
}
type TerminateModel = Api.Workflow.TerminateTaskOperateParams;
const terminateModel: TerminateModel = reactive(createDefaultTerminateModel());
function createDefaultTerminateModel(): TerminateModel {
return {
taskId: null,
comment: ''
};
}
function handleTransferConfirm(ids: CommonType.IdType[]) {
if (ids.length === 0) {
window.$message?.error('请选择转办用户');
return;
}
model.userId = ids[0];
window.$dialog?.warning({
title: '提示',
content: '是否确认转办?',
positiveText: '确认转办',
positiveButtonProps: {
type: 'primary'
},
negativeText: '取消',
onPositiveClick: async () => {
const { error } = await fetchTaskOperate(model, 'transferTask');
if (error) return;
window.$message?.success('转办成功');
visible.value = false;
emit('refresh');
}
});
}
function handleTerminate() {
window.$dialog?.warning({
title: '提示',
content: '是否确认中止?',
positiveText: '确认',
positiveButtonProps: {
type: 'primary'
},
negativeText: '取消',
onPositiveClick: async () => {
const { error } = await fetchTerminateTask(terminateModel);
if (error) return;
window.$message?.success('中止成功');
visible.value = false;
emit('refresh');
}
});
}
watch(visible, () => {
if (visible.value) {
Object.assign(model, createDefaultModel());
model.taskId = props.rowData.id;
Object.assign(terminateModel, createDefaultTerminateModel());
terminateModel.taskId = props.rowData.id;
}
});
</script>
<template>
@ -37,7 +125,7 @@ const visible = defineModel<boolean>('visible', {
<GroupTag :value="props.rowData.assigneeNames" />
</NDescriptionsItem>
<NDescriptionsItem label="版本号">
{{ props.rowData.version }}
<NTag type="info" size="small">v{{ props.rowData.version }}.0</NTag>
</NDescriptionsItem>
<NDescriptionsItem label="业务ID">
{{ props.rowData.businessId }}
@ -45,19 +133,25 @@ const visible = defineModel<boolean>('visible', {
</NDescriptions>
<template #footer>
<NSpace justify="end" :size="16">
<NButton :disabled="btnLoading" type="primary" @click="openTransferModal">转办</NButton>
<NButton :disabled="btnLoading" type="primary" @click="openMultiInstanceModal">加签</NButton>
<NButton :disabled="btnLoading" type="primary">减签</NButton>
<NButton :disabled="btnLoading" type="error">中止</NButton>
<NButton v-if="isWaiting" type="primary" @click="openTransferModal">转办</NButton>
<NButton v-if="isWaiting && isTicketOrSignInstance" type="primary" @click="openMultiInstanceModal">
加签
</NButton>
<NButton v-if="isWaiting && isTicketOrSignInstance" type="primary">减签</NButton>
<NButton v-if="isWaiting" type="error" @click="handleTerminate">中止</NButton>
</NSpace>
</template>
<!-- 转办用户选择器 -->
<UserSelectModal
v-model:visible="transferVisible"
:disabled-ids="props.rowData.assigneeIds.split(',')"
@confirm="handleTransferConfirm"
/>
<!-- 加签用户选择器 -->
<UserSelectModal
v-model:visible="multiInstanceVisible"
multiple
:disabled-ids="props.rowData.assigneeIds.split(',')"
/>
<!-- 转办用户选择器 -->
<UserSelectModal v-model:visible="transferVisible" :disabled-ids="props.rowData.assigneeIds.split(',')" />
</NModal>
</template>

View File

@ -43,3 +43,21 @@ export function fetchGetAllFinishedTask(data: Api.Workflow.TaskSearchParams) {
params: data
});
}
/** 任务操作 */
export function fetchTaskOperate(data: Api.Workflow.TaskOperateParams, operateType: Api.Workflow.TaskOperateType) {
return request<Api.Workflow.Task>({
url: `/workflow/task/taskOperation/${operateType}`,
method: 'post',
data
});
}
/** 终止任务 */
export function fetchTerminateTask(data: Api.Workflow.TerminateTaskOperateParams) {
return request<boolean>({
url: '/workflow/task/terminationTask',
method: 'post',
data
});
}

View File

@ -312,6 +312,23 @@ declare namespace Api {
/** 任务列表 */
type TaskList = Common.PaginatingQueryRecord<Task>;
/** 任务操作类型 */
type TaskOperateType = 'delegateTask' | 'transferTask' | 'addSignature' | 'reductionSignature' | 'stopTask';
/** 任务操作参数 */
type TaskOperateParams = CommonType.RecordNullable<{
taskId: CommonType.IdType;
userId?: CommonType.IdType;
userIds?: CommonType.IdType[];
message?: string;
}>;
/** 终止任务 */
type TerminateTaskOperateParams = CommonType.RecordNullable<{
taskId: CommonType.IdType;
comment?: string;
}>;
/** 协作方式 */
type CooperateType = 1 | 2 | 3 | 4 | 5 | 6 | 7;

View File

@ -26,8 +26,8 @@ defineOptions({
useDict('wf_business_status');
useDict('wf_task_status');
const appStore = useAppStore();
const { bool: viewVisible, setTrue: showViewDrawer } = useBoolean(false);
const { bool: interveneVisible, setTrue: showInterveneDrawer } = useBoolean(false);
const { bool: viewVisible, setTrue: showViewDrawer } = useBoolean();
const { bool: interveneVisible, setTrue: showInterveneDrawer } = useBoolean();
const dynamicComponent = shallowRef();
type Task = Api.Workflow.Task;
@ -304,7 +304,11 @@ function handleIntervene(row: Api.Workflow.TaskOrHisTask) {
class="sm:h-full"
/>
<component :is="dynamicComponent" :visible="viewVisible" operate-type="detail" :business-id="businessId" />
<FlowInterveneModal v-model:visible="interveneVisible" :row-data="interveneRowData as Task" />
<FlowInterveneModal
v-model:visible="interveneVisible"
:row-data="interveneRowData as Task"
@refresh="getData"
/>
</NCard>
</div>
</TableSiderLayout>