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 { import type {
CompleteTaskReqData, CompleteTaskReqData,
NextNodeInfo,
StartWorkFlowReqData, StartWorkFlowReqData,
TaskInfo, TaskInfo,
TaskOperationData, TaskOperationData,
@ -156,3 +157,16 @@ export function getBackTaskNode(definitionId: string, nodeCode: string) {
export function currentTaskAllUser(taskId: ID) { export function currentTaskAllUser(taskId: ID) {
return requestClient.get<any>(`/workflow/task/currentTaskAllUser/${taskId}`); 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; variables: any;
// 附件ID 1,2,3,4形式 // 附件ID 1,2,3,4形式
fileId?: string; fileId?: string;
// 选人 key为节点code value为用户ID join(,)
assigneeMap: { [key: string]: string };
} }
export interface StartWorkFlowReqData { export interface StartWorkFlowReqData {
@ -79,3 +81,28 @@ export type TaskOperationType =
| 'delegateTask' | 'delegateTask'
| 'reductionSignature' | 'reductionSignature'
| 'transferTask'; | '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"> <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 { useVbenModal } from '@vben/common-ui';
import { cloneDeep } from '@vben/utils'; import { cloneDeep } from '@vben/utils';
import { message } from 'ant-design-vue';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';
import { completeTask } from '#/api/workflow/task'; import { completeTask, getNextNodeList } from '#/api/workflow/task';
import { CopyComponent } from '.'; import { CopyComponent } from '.';
@ -77,6 +84,11 @@ const [BasicForm, formApi] = useVbenForm({
defaultValue: [], defaultValue: [],
label: '抄送人', label: '抄送人',
}, },
{
fieldName: 'assigneeMap',
component: 'Input',
label: '下一步审批人',
},
{ {
fieldName: 'message', fieldName: 'message',
component: 'Textarea', component: 'Textarea',
@ -92,8 +104,12 @@ interface ModalProps {
taskId: string; taskId: string;
// //
copyPermission: boolean; copyPermission: boolean;
//
assignPermission: boolean;
} }
// v-for
const nextNodeInfo = ref<(NextNodeInfo & { selectUserList: User[] })[]>([]);
const [BasicModal, modalApi] = useVbenModal({ const [BasicModal, modalApi] = useVbenModal({
title: '审批通过', title: '审批通过',
fullscreenButton: false, fullscreenButton: false,
@ -106,18 +122,36 @@ const [BasicModal, modalApi] = useVbenModal({
} }
modalApi.modalLoading(true); modalApi.modalLoading(true);
const { taskId, copyPermission } = modalApi.getData() as ModalProps; const { taskId, copyPermission, assignPermission } =
modalApi.getData() as ModalProps;
// //
formApi.updateSchema([ formApi.updateSchema([
{ {
fieldName: 'flowCopyList', fieldName: 'flowCopyList',
dependencies: { dependencies: {
show: copyPermission, if: copyPermission,
triggerFields: [''],
},
},
{
fieldName: 'assigneeMap',
dependencies: {
if: assignPermission,
triggerFields: [''], triggerFields: [''],
}, },
}, },
]); ]);
//
if (assignPermission) {
const resp = await getNextNodeList({ taskId });
nextNodeInfo.value = resp.map((item) => ({
...item,
//
selectUserList: [],
}));
}
await formApi.setFieldValue('taskId', taskId); await formApi.setFieldValue('taskId', taskId);
modalApi.modalLoading(false); modalApi.modalLoading(false);
@ -144,6 +178,26 @@ async function handleSubmit() {
variables: {}, variables: {},
flowCopyList, flowCopyList,
} as CompleteTaskReqData; } 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); await completeTask(requestData);
modalApi.close(); modalApi.close();
emit('complete'); emit('complete');
@ -161,6 +215,24 @@ async function handleSubmit() {
<template #flowCopyList="slotProps"> <template #flowCopyList="slotProps">
<CopyComponent v-model:user-list="slotProps.modelValue" /> <CopyComponent v-model:user-list="slotProps.modelValue" />
</template> </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> </BasicForm>
</BasicModal> </BasicModal>
</template> </template>

View File

@ -256,7 +256,13 @@ const [ApprovalModal, approvalModalApi] = useVbenModal({
function handleApproval() { function handleApproval() {
// //
const copyPermission = buttonPermissions.value?.copy ?? false; 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(); approvalModalApi.open();
} }

View File

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

View File

@ -18,9 +18,16 @@ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}); });
const props = withDefaults(defineProps<{ mode?: 'multiple' | 'single' }>(), { const props = withDefaults(
defineProps<{ allowUserIds?: string; mode?: 'multiple' | 'single' }>(),
{
mode: 'multiple', mode: 'multiple',
}); /**
* 允许选择允许选择的人员ID 会当做参数拼接在uselist接口
*/
allowUserIds: '',
},
);
const emit = defineEmits<{ const emit = defineEmits<{
/** /**
@ -136,11 +143,17 @@ const gridOptions: VxeGridProps = {
} }
} }
return await userList({ const params: any = {
pageNum: page.currentPage, pageNum: page.currentPage,
pageSize: page.pageSize, pageSize: page.pageSize,
...formValues, ...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 emptyImage = Empty.PRESENTED_IMAGE_SIMPLE;
const taskList = ref<({ active: boolean } & TaskInfo)[]>([]); const taskList = ref<(TaskInfo & { active: boolean })[]>([]);
const taskTotal = ref(0); const taskTotal = ref(0);
const page = ref(1); const page = ref(1);
const loading = ref(false); const loading = ref(false);

View File

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

View File

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

View File

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