feat:【BPM 工作流】 完善流转记录列表
parent
e39e5a4ed8
commit
66ac3de5c1
|
@ -82,8 +82,10 @@ export const rejectTask = async (data: any) => {
|
|||
};
|
||||
|
||||
/** 根据流程实例 ID 查询任务列表 */
|
||||
export const getTaskListByProcessInstanceId = async (data: any) => {
|
||||
return await requestClient.get('/bpm/task/list-by-process-instance-id', data);
|
||||
export const getTaskListByProcessInstanceId = async (id: string) => {
|
||||
return await requestClient.get(
|
||||
`/bpm/task/list-by-process-instance-id?processInstanceId=${id}`,
|
||||
);
|
||||
};
|
||||
|
||||
/** 获取所有可退回的节点 */
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
|
||||
import type { SystemUserApi } from '#/api/system/user';
|
||||
|
||||
import { nextTick, onMounted, ref } from 'vue';
|
||||
import { nextTick, onMounted, ref, shallowRef, watch } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
@ -39,6 +39,7 @@ import {
|
|||
SvgBpmRunningIcon,
|
||||
} from '#/views/bpm/processInstance/detail/modules/icons';
|
||||
|
||||
import BpmProcessInstanceTaskList from './modules/task-list.vue';
|
||||
import ProcessInstanceTimeline from './modules/time-line.vue';
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceDetail' });
|
||||
|
@ -99,7 +100,7 @@ const detailForm = ref({
|
|||
const writableFields: Array<string> = []; // 表单可以编辑的字段
|
||||
|
||||
/** 加载流程实例 */
|
||||
const BusinessFormComponent = ref<any>(null); // 异步组件
|
||||
const BusinessFormComponent = shallowRef<any>(null); // 异步组件
|
||||
|
||||
/** 获取详情 */
|
||||
async function getDetail() {
|
||||
|
@ -221,6 +222,20 @@ const setFieldPermission = (field: string, permission: string) => {
|
|||
|
||||
/** 当前的Tab */
|
||||
const activeTab = ref('form');
|
||||
const taskListRef = ref();
|
||||
|
||||
// 监听 Tab 切换,当切换到 "record" 标签时刷新任务列表
|
||||
watch(
|
||||
() => activeTab.value,
|
||||
(newVal) => {
|
||||
if (newVal === 'record') {
|
||||
// 如果切换到流转记录标签,刷新任务列表
|
||||
nextTick(() => {
|
||||
taskListRef.value?.refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/** 初始化 */
|
||||
const userOptions = ref<SystemUserApi.User[]>([]); // 用户列表
|
||||
|
@ -332,7 +347,13 @@ onMounted(async () => {
|
|||
</TabPane>
|
||||
|
||||
<TabPane tab="流转记录" key="record">
|
||||
<div>待开发</div>
|
||||
<div class="h-full">
|
||||
<BpmProcessInstanceTaskList
|
||||
ref="taskListRef"
|
||||
:loading="processInstanceLoading"
|
||||
:id="id"
|
||||
/>
|
||||
</div>
|
||||
</TabPane>
|
||||
|
||||
<!-- TODO 待开发 -->
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
<script setup lang="ts">
|
||||
import type { formCreate } from '@form-create/antd-designer';
|
||||
|
||||
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||
|
||||
import type { BpmTaskApi } from '#/api/bpm/task';
|
||||
|
||||
import { nextTick, onMounted, ref, shallowRef } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { Button } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { getTaskListByProcessInstanceId } from '#/api/bpm/task';
|
||||
import { DICT_TYPE, formatPast2, setConfAndFields2 } from '#/utils';
|
||||
|
||||
defineOptions({
|
||||
name: 'BpmProcessInstanceTaskList',
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
id: string;
|
||||
loading: boolean;
|
||||
}>();
|
||||
|
||||
// 使用shallowRef减少不必要的深度响应
|
||||
const columns = shallowRef([
|
||||
{
|
||||
field: 'name',
|
||||
title: '审批节点',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'approver',
|
||||
title: '审批人',
|
||||
slots: {
|
||||
default: ({ row }: { row: BpmTaskApi.TaskManagerVO }) => {
|
||||
return row.assigneeUser?.nickname || row.ownerUser?.nickname;
|
||||
},
|
||||
},
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '开始时间',
|
||||
formatter: 'formatDateTime',
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
field: 'endTime',
|
||||
title: '结束时间',
|
||||
formatter: 'formatDateTime',
|
||||
minWidth: 180,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '审批状态',
|
||||
minWidth: 150,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.BPM_TASK_STATUS },
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'reason',
|
||||
title: '审批建议',
|
||||
slots: {
|
||||
default: 'slot-reason',
|
||||
},
|
||||
minWidth: 200,
|
||||
},
|
||||
{
|
||||
field: 'durationInMillis',
|
||||
title: '耗时',
|
||||
minWidth: 180,
|
||||
slots: {
|
||||
default: ({ row }: { row: BpmTaskApi.TaskManagerVO }) => {
|
||||
return formatPast2(row.durationInMillis);
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Grid配置和API
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {
|
||||
columns: columns.value,
|
||||
keepSource: true,
|
||||
showFooter: true,
|
||||
border: true,
|
||||
height: 'auto',
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async () => {
|
||||
return await getTaskListByProcessInstanceId(props.id);
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbarConfig: {
|
||||
enabled: false,
|
||||
},
|
||||
cellConfig: {
|
||||
height: 60,
|
||||
},
|
||||
} as VxeTableGridOptions<BpmTaskApi.TaskVO>,
|
||||
});
|
||||
|
||||
/**
|
||||
* 刷新表格数据
|
||||
*/
|
||||
const refresh = (): void => {
|
||||
gridApi.query();
|
||||
};
|
||||
|
||||
// 表单相关
|
||||
interface TaskForm {
|
||||
rule: any[];
|
||||
option: Record<string, any>;
|
||||
value: Record<string, any>;
|
||||
}
|
||||
|
||||
// 定义表单组件引用类型
|
||||
|
||||
// 使用明确的类型定义
|
||||
const formRef = ref<formCreate>();
|
||||
const taskForm = ref<TaskForm>({
|
||||
rule: [],
|
||||
option: {},
|
||||
value: {},
|
||||
});
|
||||
|
||||
/**
|
||||
* 显示表单详情
|
||||
* @param row 任务数据
|
||||
*/
|
||||
async function showFormDetail(row: BpmTaskApi.TaskManagerVO): Promise<void> {
|
||||
// 设置表单配置和表单字段
|
||||
taskForm.value = {
|
||||
rule: [],
|
||||
option: {},
|
||||
value: row,
|
||||
};
|
||||
|
||||
setConfAndFields2(
|
||||
taskForm,
|
||||
row.formConf,
|
||||
row.formFields || [],
|
||||
row.formVariables || {},
|
||||
);
|
||||
|
||||
// 打开弹窗
|
||||
modalApi.open();
|
||||
|
||||
// 等待表单渲染
|
||||
await nextTick();
|
||||
|
||||
// 获取表单API实例
|
||||
const formApi = formRef.value?.fapi;
|
||||
if (!formApi) return;
|
||||
|
||||
// 设置表单不可编辑
|
||||
formApi.btn.show(false);
|
||||
formApi.resetBtn.show(false);
|
||||
formApi.disabled(true);
|
||||
}
|
||||
|
||||
// 表单查看模态框
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
title: '查看表单',
|
||||
footer: false,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
});
|
||||
|
||||
// 暴露刷新方法给父组件
|
||||
defineExpose({
|
||||
refresh,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full flex-col">
|
||||
<Grid>
|
||||
<template #slot-reason="{ row }">
|
||||
<div class="flex flex-wrap items-center justify-center">
|
||||
<span v-if="row.reason">{{ row.reason }}</span>
|
||||
<span v-else>-</span>
|
||||
|
||||
<Button
|
||||
v-if="row.formId > 0"
|
||||
type="primary"
|
||||
@click="showFormDetail(row)"`
|
||||
size="small"
|
||||
ghost
|
||||
class="ml-1"
|
||||
>
|
||||
<IconifyIcon icon="ep:document" />
|
||||
<span class="!ml-[2px] text-[12px]">查看表单</span>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Grid>
|
||||
<Modal class="w-[800px]">
|
||||
<form-create
|
||||
ref="formRef"
|
||||
v-model="taskForm.value"
|
||||
:option="taskForm.option"
|
||||
:rule="taskForm.rule"
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
Loading…
Reference in New Issue