This commit is contained in:
xingyu4j
2025-10-21 12:12:05 +08:00
12 changed files with 867 additions and 3 deletions

View File

@@ -6,7 +6,10 @@ export namespace BpmProcessDefinitionApi {
/** 流程定义 */
export interface ProcessDefinition {
id: string;
key?: string;
version: number;
name: string;
description: string;
deploymentTime: number;
suspensionState: number;
modelType: number;
@@ -15,6 +18,7 @@ export namespace BpmProcessDefinitionApi {
bpmnXml?: string;
simpleModel?: string;
formFields?: string[];
icon?: string;
}
}

View File

@@ -52,7 +52,7 @@ export interface ModelCategoryInfo {
}
/** 获取流程模型列表 */
export async function getModelList(name: string | undefined) {
export async function getModelList(name?: string) {
return requestClient.get<BpmModelApi.Model[]>('/bpm/model/list', {
params: { name },
});

View File

@@ -1,5 +1,8 @@
import type {
BpmCandidateStrategyEnum,
BpmNodeTypeEnum,
} from '@vben/constants';
import type { PageParam, PageResult } from '@vben/request';
import type { BpmCandidateStrategyEnum, BpmNodeTypeEnum } from '@vben/utils';
import type { BpmTaskApi } from '../task';
@@ -35,11 +38,12 @@ export namespace BpmProcessInstanceApi {
candidateStrategy?: BpmCandidateStrategyEnum;
candidateUsers?: User[];
endTime?: Date;
id: number;
id: string;
name: string;
nodeType: BpmNodeTypeEnum;
startTime?: Date;
status: number;
processInstanceId?: string;
tasks: ApprovalTaskInfo[];
}

View File

@@ -13,6 +13,7 @@ export namespace BpmTaskApi {
status: number; // 监听器状态
event: string; // 监听事件
valueType: string; // 监听器值类型
processInstance?: BpmProcessInstanceApi.ProcessInstance; // 流程实例
}
// 流程任务
@@ -130,3 +131,10 @@ export const getChildrenTaskList = async (id: string) => {
`/bpm/task/list-by-parent-task-id?parentTaskId=${id}`,
);
};
// 撤回任务
export const withdrawTask = async (taskId: string) => {
return await requestClient.put('/bpm/task/withdraw', null, {
params: { taskId },
});
};

View File

@@ -0,0 +1,92 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'name',
label: '流程名称',
component: 'Input',
componentProps: {
placeholder: '请输入流程名称',
allowClear: true,
},
},
{
fieldName: 'createTime',
label: '抄送时间',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];
}
/** 列表的字段 */
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'processInstanceName',
title: '流程名称',
minWidth: 200,
},
{
field: 'summary',
title: '摘要',
minWidth: 200,
formatter: ({ cellValue }) => {
return cellValue && cellValue.length > 0
? cellValue
.map((item: any) => `${item.key} : ${item.value}`)
.join('\n')
: '-';
},
},
{
field: 'startUser.nickname',
title: '流程发起人',
minWidth: 120,
},
{
field: 'processInstanceStartTime',
title: '流程发起时间',
minWidth: 180,
formatter: 'formatDateTime',
},
{
field: 'activityName',
title: '抄送节点',
minWidth: 120,
},
{
field: 'createUser.nickname',
title: '抄送人',
minWidth: 120,
formatter: ({ cellValue }) => {
return cellValue || '-';
},
},
{
field: 'reason',
title: '抄送意见',
minWidth: 180,
},
{
field: 'createTime',
title: '抄送时间',
minWidth: 180,
formatter: 'formatDateTime',
},
{
title: '操作',
width: 120,
fixed: 'right',
slots: { default: 'actions' },
},
];
}

View File

@@ -0,0 +1,85 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
import { DocAlert, Page } from '@vben/common-ui';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getProcessInstanceCopyPage } from '#/api/bpm/processInstance';
import { $t } from '#/locales';
import { router } from '#/router';
import { useGridColumns, useGridFormSchema } from './data';
defineOptions({ name: 'BpmCopyTask' });
/** 任务详情 */
function handleDetail(row: BpmProcessInstanceApi.Copy) {
const query = {
id: row.processInstanceId,
...(row.activityId && { activityId: row.activityId }),
};
router.push({
name: 'BpmProcessInstanceDetail',
query,
});
}
const [Grid] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getProcessInstanceCopyPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<BpmProcessInstanceApi.Copy>,
});
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert
title="审批转办、委派、抄送"
url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
/>
</template>
<Grid table-title="抄送任务">
<template #actions="{ row }">
<TableAction
:actions="[
{
label: $t('common.detail'),
type: 'primary',
link: true,
icon: ACTION_ICON.VIEW,
auth: ['bpm:task:query'],
onClick: handleDetail.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@@ -0,0 +1,154 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { getCategorySimpleList } from '#/api/bpm/category';
import { getSimpleProcessDefinitionList } from '#/api/bpm/definition';
import { getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'name',
label: '任务名称',
component: 'Input',
componentProps: {
placeholder: '请输入任务名称',
allowClear: true,
},
},
{
fieldName: 'processDefinitionKey',
label: '所属流程',
component: 'ApiSelect',
componentProps: {
placeholder: '请选择流程定义',
allowClear: true,
api: getSimpleProcessDefinitionList,
labelField: 'name',
valueField: 'key',
},
},
{
fieldName: 'category',
label: '流程分类',
component: 'ApiSelect',
componentProps: {
placeholder: '请输入流程分类',
allowClear: true,
api: getCategorySimpleList,
labelField: 'name',
valueField: 'code',
},
},
{
fieldName: 'status',
label: '审批状态',
component: 'Select',
componentProps: {
options: getDictOptions(DICT_TYPE.BPM_TASK_STATUS, 'number'),
placeholder: '请选择审批状态',
allowClear: true,
},
},
{
fieldName: 'createTime',
label: '发起时间',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];
}
/** 列表的字段 */
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'processInstance.name',
title: '流程',
minWidth: 200,
},
{
field: 'processInstance.summary',
title: '摘要',
minWidth: 200,
formatter: ({ cellValue }) => {
return cellValue && cellValue.length > 0
? cellValue
.map((item: any) => `${item.key} : ${item.value}`)
.join('\n')
: '-';
},
},
{
field: 'processInstance.startUser.nickname',
title: '发起人',
minWidth: 120,
},
{
field: 'processInstance.createTime',
title: '发起时间',
minWidth: 180,
formatter: 'formatDateTime',
},
{
field: 'name',
title: '当前任务',
minWidth: 180,
},
{
field: 'createTime',
title: '任务开始时间',
minWidth: 180,
formatter: 'formatDateTime',
},
{
field: 'endTime',
title: '任务结束时间',
minWidth: 180,
formatter: 'formatDateTime',
},
{
field: 'status',
title: '审批状态',
minWidth: 180,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.BPM_TASK_STATUS },
},
},
{
field: 'reason',
title: '审批建议',
minWidth: 180,
},
{
field: 'durationInMillis',
title: '耗时',
minWidth: 180,
formatter: 'formatPast2',
},
{
field: 'processInstanceId',
title: '流程编号',
minWidth: 280,
},
{
field: 'id',
title: '任务编号',
minWidth: 280,
},
{
title: '操作',
width: 120,
fixed: 'right',
slots: { default: 'actions' },
},
];
}

View File

@@ -0,0 +1,114 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmTaskApi } from '#/api/bpm/task';
import { DocAlert, Page } from '@vben/common-ui';
import { ElLoading, ElMessage } from 'element-plus';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getTaskDonePage, withdrawTask } from '#/api/bpm/task';
import { router } from '#/router';
import { useGridColumns, useGridFormSchema } from './data';
defineOptions({ name: 'BpmDoneTask' });
/** 查看历史 */
function handleHistory(row: BpmTaskApi.TaskManager) {
router.push({
name: 'BpmProcessInstanceDetail',
query: {
id: row.processInstance.id,
taskId: row.id,
},
});
}
/** 撤回任务 */
async function handleWithdraw(row: BpmTaskApi.TaskManager) {
const loadingInstance = ElLoading.service({
text: '正在撤回中...',
});
try {
await withdrawTask(row.id);
ElMessage.success('撤回成功');
await gridApi.query();
} finally {
loadingInstance.close();
}
}
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getTaskDonePage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<BpmTaskApi.TaskManager>,
});
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert
title="审批通过、不通过、驳回"
url="https://doc.iocoder.cn/bpm/task-todo-done/"
/>
<DocAlert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
<DocAlert
title="审批转办、委派、抄送"
url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
/>
<DocAlert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
</template>
<Grid table-title="已办任务">
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '撤回',
type: 'danger',
link: true,
icon: ACTION_ICON.DELETE,
popConfirm: {
title: '确定要撤回该任务吗?',
confirm: handleWithdraw.bind(null, row),
},
},
{
label: '历史',
type: 'primary',
link: true,
icon: ACTION_ICON.VIEW,
onClick: handleHistory.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@@ -0,0 +1,104 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE } from '@vben/constants';
import { getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'name',
label: '任务名称',
component: 'Input',
componentProps: {
placeholder: '请输入任务名称',
allowClear: true,
},
},
{
fieldName: 'createTime',
label: '创建时间',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];
}
/** 列表的字段 */
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'processInstance.name',
title: '流程',
minWidth: 200,
},
{
field: 'processInstance.startUser.nickname',
title: '发起人',
minWidth: 120,
},
{
field: 'name',
title: '当前任务',
minWidth: 180,
},
{
field: 'createTime',
title: '任务开始时间',
minWidth: 180,
formatter: 'formatDateTime',
},
{
field: 'endTime',
title: '任务结束时间',
minWidth: 180,
formatter: 'formatDateTime',
},
{
field: 'assigneeUser.nickname',
title: '审批人',
minWidth: 180,
},
{
field: 'status',
title: '审批状态',
minWidth: 180,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.BPM_TASK_STATUS },
},
},
{
field: 'reason',
title: '审批建议',
minWidth: 180,
},
{
field: 'durationInMillis',
title: '耗时',
minWidth: 180,
formatter: 'formatPast2',
},
{
field: 'processInstanceId',
title: '流程编号',
minWidth: 280,
},
{
field: 'id',
title: '任务编号',
minWidth: 280,
},
{
title: '操作',
width: 120,
fixed: 'right',
slots: { default: 'actions' },
},
];
}

View File

@@ -0,0 +1,79 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmTaskApi } from '#/api/bpm/task';
import { DocAlert, Page } from '@vben/common-ui';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getTaskManagerPage } from '#/api/bpm/task';
import { router } from '#/router';
import { useGridColumns, useGridFormSchema } from './data';
defineOptions({ name: 'BpmManagerTask' });
/** 查看历史 */
function handleHistory(row: BpmTaskApi.TaskManager) {
router.push({
name: 'BpmProcessInstanceDetail',
query: {
id: row.processInstance.id,
},
});
}
const [Grid] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getTaskManagerPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<BpmTaskApi.TaskManager>,
});
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
</template>
<Grid table-title="流程任务">
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '历史',
type: 'primary',
link: true,
icon: ACTION_ICON.VIEW,
auth: ['bpm:task:query'],
onClick: handleHistory.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>
</template>

View File

@@ -0,0 +1,131 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { getCategorySimpleList } from '#/api/bpm/category';
import { getSimpleProcessDefinitionList } from '#/api/bpm/definition';
import { getRangePickerDefaultProps } from '#/utils';
/** 列表的搜索表单 */
export function useGridFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'name',
label: '任务名称',
component: 'Input',
componentProps: {
placeholder: '请输入任务名称',
allowClear: true,
},
},
{
fieldName: 'processDefinitionKey',
label: '所属流程',
component: 'ApiSelect',
componentProps: {
placeholder: '请选择流程定义',
allowClear: true,
api: getSimpleProcessDefinitionList,
labelField: 'name',
valueField: 'key',
},
},
{
fieldName: 'category',
label: '流程分类',
component: 'ApiSelect',
componentProps: {
placeholder: '请输入流程分类',
allowClear: true,
api: getCategorySimpleList,
labelField: 'name',
valueField: 'code',
},
},
{
fieldName: 'status',
label: '流程状态',
component: 'Select',
componentProps: {
options: getDictOptions(
DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS,
'number',
),
placeholder: '请选择流程状态',
allowClear: true,
},
},
{
fieldName: 'createTime',
label: '发起时间',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
allowClear: true,
},
},
];
}
/** 列表的字段 */
export function useGridColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'processInstance.name',
title: '流程',
minWidth: 200,
},
{
field: 'processInstance.summary',
title: '摘要',
minWidth: 200,
formatter: ({ cellValue }) => {
return cellValue && cellValue.length > 0
? cellValue
.map((item: any) => `${item.key} : ${item.value}`)
.join('\n')
: '-';
},
},
{
field: 'processInstance.startUser.nickname',
title: '发起人',
minWidth: 120,
},
{
field: 'processInstance.createTime',
title: '发起时间',
minWidth: 180,
formatter: 'formatDateTime',
},
{
field: 'name',
title: '当前任务',
minWidth: 180,
},
{
field: 'createTime',
title: '任务时间',
minWidth: 180,
formatter: 'formatDateTime',
},
{
field: 'processInstanceId',
title: '流程编号',
minWidth: 280,
},
{
field: 'id',
title: '任务编号',
minWidth: 280,
},
{
title: '操作',
width: 120,
fixed: 'right',
slots: { default: 'actions' },
},
];
}

View File

@@ -0,0 +1,89 @@
<script lang="ts" setup>
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { BpmTaskApi } from '#/api/bpm/task';
import { DocAlert, Page } from '@vben/common-ui';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { getTaskTodoPage } from '#/api/bpm/task';
import { router } from '#/router';
import { useGridColumns, useGridFormSchema } from './data';
defineOptions({ name: 'BpmTodoTask' });
/** 办理任务 */
function handleAudit(row: BpmTaskApi.Task) {
router.push({
name: 'BpmProcessInstanceDetail',
query: {
id: row.processInstance!.id,
taskId: row.id,
},
});
}
const [Grid] = useVbenVxeGrid({
formOptions: {
schema: useGridFormSchema(),
},
gridOptions: {
columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getTaskTodoPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<BpmTaskApi.Task>,
});
</script>
<template>
<Page auto-content-height>
<template #doc>
<DocAlert
title="审批通过、不通过、驳回"
url="https://doc.iocoder.cn/bpm/task-todo-done/"
/>
<DocAlert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
<DocAlert
title="审批转办、委派、抄送"
url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
/>
<DocAlert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
</template>
<Grid table-title="待办任务">
<template #actions="{ row }">
<TableAction
:actions="[
{
label: '办理',
type: 'primary',
link: true,
icon: ACTION_ICON.VIEW,
auth: ['bpm:task:query'],
onClick: handleAudit.bind(null, row),
},
]"
/>
</template>
</Grid>
</Page>
</template>