feat: 选择下一步审批人权限

This commit is contained in:
dap 2025-03-08 12:08:01 +08:00
parent e78f4e984d
commit 6170da0870
10 changed files with 167 additions and 22 deletions

View File

@ -1,5 +1,6 @@
import type {
CompleteTaskReqData,
NextNodeInfo,
StartWorkFlowReqData,
TaskInfo,
TaskOperationData,
@ -156,3 +157,16 @@ export function getBackTaskNode(definitionId: string, nodeCode: string) {
export function currentTaskAllUser(taskId: ID) {
return requestClient.get<any>(`/workflow/task/currentTaskAllUser/${taskId}`);
}
/**
*
* @param data data
* @param data.taskId taskId
* @returns NextNodeInfo
*/
export function getNextNodeList(data: { taskId: string }) {
return requestClient.post<NextNodeInfo[]>(
'/workflow/task/getNextNodeList',
data,
);
}

View File

@ -45,6 +45,8 @@ export interface CompleteTaskReqData {
variables: any;
// 附件ID 1,2,3,4形式
fileId?: string;
// 选人 key为节点code value为用户ID join(,)
assigneeMap: { [key: string]: string };
}
export interface StartWorkFlowReqData {
@ -79,3 +81,28 @@ export type TaskOperationType =
| 'delegateTask'
| 'reductionSignature'
| 'transferTask';
export interface NextNodeInfo {
skipList: string[];
id: string;
createTime: string;
updateTime: string;
tenantId: string;
delFlag: string;
nodeType: number;
definitionId: string;
nodeCode: string;
nodeName: string;
permissionFlag: string;
nodeRatio: string;
coordinate: string;
version: string;
anyNodeSkip: any;
listenerType: any;
listenerPath: any;
handlerType: any;
handlerPath: any;
formCustom: string;
formPath: any;
ext: string;
}

View File

@ -1,14 +1,21 @@
<!-- 审批同意的弹窗 -->
<script setup lang="ts">
import type { CompleteTaskReqData } from '#/api/workflow/task/model';
import type { User } from '#/api/system/user/model';
import type {
CompleteTaskReqData,
NextNodeInfo,
} from '#/api/workflow/task/model';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { cloneDeep } from '@vben/utils';
import { message } from 'ant-design-vue';
import { omit } from 'lodash-es';
import { useVbenForm } from '#/adapter/form';
import { completeTask } from '#/api/workflow/task';
import { completeTask, getNextNodeList } from '#/api/workflow/task';
import { CopyComponent } from '.';
@ -77,6 +84,11 @@ const [BasicForm, formApi] = useVbenForm({
defaultValue: [],
label: '抄送人',
},
{
fieldName: 'assigneeMap',
component: 'Input',
label: '下一步审批人',
},
{
fieldName: 'message',
component: 'Textarea',
@ -92,8 +104,12 @@ interface ModalProps {
taskId: string;
//
copyPermission: boolean;
//
assignPermission: boolean;
}
// v-for
const nextNodeInfo = ref<(NextNodeInfo & { selectUserList: User[] })[]>([]);
const [BasicModal, modalApi] = useVbenModal({
title: '审批通过',
fullscreenButton: false,
@ -106,18 +122,36 @@ const [BasicModal, modalApi] = useVbenModal({
}
modalApi.modalLoading(true);
const { taskId, copyPermission } = modalApi.getData() as ModalProps;
const { taskId, copyPermission, assignPermission } =
modalApi.getData() as ModalProps;
//
formApi.updateSchema([
{
fieldName: 'flowCopyList',
dependencies: {
show: copyPermission,
if: copyPermission,
triggerFields: [''],
},
},
{
fieldName: 'assigneeMap',
dependencies: {
if: assignPermission,
triggerFields: [''],
},
},
]);
//
if (assignPermission) {
const resp = await getNextNodeList({ taskId });
nextNodeInfo.value = resp.map((item) => ({
...item,
//
selectUserList: [],
}));
}
await formApi.setFieldValue('taskId', taskId);
modalApi.modalLoading(false);
@ -144,6 +178,26 @@ async function handleSubmit() {
variables: {},
flowCopyList,
} as CompleteTaskReqData;
//
if (modalApi.getData()?.assignPermission) {
//
for (const item of nextNodeInfo.value) {
if (item.selectUserList.length === 0) {
message.warn(`未选择节点[${item.nodeName}]审批人`);
return;
}
}
const assigneeMap: { [key: string]: string } = {};
nextNodeInfo.value.forEach((item) => {
assigneeMap[item.nodeCode] = item.selectUserList
.map((u) => u.userId)
.join(',');
});
requestData.assigneeMap = assigneeMap;
}
await completeTask(requestData);
modalApi.close();
emit('complete');
@ -161,6 +215,24 @@ async function handleSubmit() {
<template #flowCopyList="slotProps">
<CopyComponent v-model:user-list="slotProps.modelValue" />
</template>
<template #assigneeMap>
<div
v-for="item in nextNodeInfo"
:key="item.nodeCode"
class="flex items-center gap-2"
>
<template v-if="item.permissionFlag">
<span class="opacity-70">{{ item.nodeName }}</span>
<CopyComponent
:allow-user-ids="item.permissionFlag"
v-model:user-list="item.selectUserList"
/>
</template>
<template v-else>
<span class="text-red-500">没有权限, 请联系管理员</span>
</template>
</div>
</template>
</BasicForm>
</BasicModal>
</template>

View File

@ -256,7 +256,13 @@ const [ApprovalModal, approvalModalApi] = useVbenModal({
function handleApproval() {
//
const copyPermission = buttonPermissions.value?.copy ?? false;
approvalModalApi.setData({ taskId: props.task?.id, copyPermission });
//
const assignPermission = buttonPermissions.value?.pop ?? false;
approvalModalApi.setData({
taskId: props.task?.id,
copyPermission,
assignPermission,
});
approvalModalApi.open();
}

View File

@ -1,8 +1,10 @@
<!--抄送组件-->
<script setup lang="ts">
import type { PropType } from 'vue';
import type { User } from '#/api/system/user/model';
import { computed, type PropType } from 'vue';
import { computed } from 'vue';
import { useVbenModal, VbenAvatar } from '@vben/common-ui';
@ -15,12 +17,19 @@ defineOptions({
inheritAttrs: false,
});
const props = withDefaults(defineProps<{ ellipseNumber?: number }>(), {
const props = withDefaults(
defineProps<{ allowUserIds?: string; ellipseNumber?: number }>(),
{
/**
* 最大显示的头像数量 超过显示为省略号头像
*/
ellipseNumber: 3,
});
/**
* 允许选择允许选择的人员ID 会当做参数拼接在uselist接口
*/
allowUserIds: '',
},
);
const emit = defineEmits<{ cancel: []; finish: [User[]] }>();
@ -80,6 +89,10 @@ const displayedList = computed(() => {
</Tooltip>
</AvatarGroup>
<a-button size="small" @click="handleOpen">选择人员</a-button>
<UserSelectModal @cancel="$emit('cancel')" @finish="handleFinish" />
<UserSelectModal
:allow-user-ids="allowUserIds"
@cancel="$emit('cancel')"
@finish="handleFinish"
/>
</div>
</template>

View File

@ -18,9 +18,16 @@ defineOptions({
inheritAttrs: false,
});
const props = withDefaults(defineProps<{ mode?: 'multiple' | 'single' }>(), {
const props = withDefaults(
defineProps<{ allowUserIds?: string; mode?: 'multiple' | 'single' }>(),
{
mode: 'multiple',
});
/**
* 允许选择允许选择的人员ID 会当做参数拼接在uselist接口
*/
allowUserIds: '',
},
);
const emit = defineEmits<{
/**
@ -136,11 +143,17 @@ const gridOptions: VxeGridProps = {
}
}
return await userList({
const params: any = {
pageNum: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
};
//
if (props.allowUserIds) {
params.userIds = props.allowUserIds;
}
return await userList(params);
},
},
},

View File

@ -27,7 +27,7 @@ import { ApprovalCard, ApprovalPanel } from '../components';
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
const taskList = ref<({ active: boolean } & TaskInfo)[]>([]);
const taskList = ref<(TaskInfo & { active: boolean })[]>([]);
const taskTotal = ref(0);
const page = ref(1);
const loading = ref(false);

View File

@ -29,7 +29,7 @@ import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
const taskList = ref<({ active: boolean } & TaskInfo)[]>([]);
const taskList = ref<(TaskInfo & { active: boolean })[]>([]);
const taskTotal = ref(0);
const page = ref(1);
const loading = ref(false);

View File

@ -29,7 +29,7 @@ import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
const taskList = ref<({ active: boolean } & TaskInfo)[]>([]);
const taskList = ref<(TaskInfo & { active: boolean })[]>([]);
const taskTotal = ref(0);
const page = ref(1);
const loading = ref(false);

View File

@ -30,7 +30,7 @@ import { ApprovalCard, ApprovalPanel, CopyComponent } from '../components';
const emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
const taskList = ref<({ active: boolean } & TaskInfo)[]>([]);
const taskList = ref<(TaskInfo & { active: boolean })[]>([]);
const taskTotal = ref(0);
const page = ref(1);
const loading = ref(false);