feat:【BPM 工作流】 完善流转记录列表
							parent
							
								
									e39e5a4ed8
								
							
						
					
					
						commit
						66ac3de5c1
					
				| 
						 | 
					@ -82,8 +82,10 @@ export const rejectTask = async (data: any) => {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 根据流程实例 ID 查询任务列表 */
 | 
					/** 根据流程实例 ID 查询任务列表 */
 | 
				
			||||||
export const getTaskListByProcessInstanceId = async (data: any) => {
 | 
					export const getTaskListByProcessInstanceId = async (id: string) => {
 | 
				
			||||||
  return await requestClient.get('/bpm/task/list-by-process-instance-id', data);
 | 
					  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 { BpmProcessInstanceApi } from '#/api/bpm/processInstance';
 | 
				
			||||||
import type { SystemUserApi } from '#/api/system/user';
 | 
					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 { Page } from '@vben/common-ui';
 | 
				
			||||||
import { formatDateTime } from '@vben/utils';
 | 
					import { formatDateTime } from '@vben/utils';
 | 
				
			||||||
| 
						 | 
					@ -39,6 +39,7 @@ import {
 | 
				
			||||||
  SvgBpmRunningIcon,
 | 
					  SvgBpmRunningIcon,
 | 
				
			||||||
} from '#/views/bpm/processInstance/detail/modules/icons';
 | 
					} from '#/views/bpm/processInstance/detail/modules/icons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import BpmProcessInstanceTaskList from './modules/task-list.vue';
 | 
				
			||||||
import ProcessInstanceTimeline from './modules/time-line.vue';
 | 
					import ProcessInstanceTimeline from './modules/time-line.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineOptions({ name: 'BpmProcessInstanceDetail' });
 | 
					defineOptions({ name: 'BpmProcessInstanceDetail' });
 | 
				
			||||||
| 
						 | 
					@ -99,7 +100,7 @@ const detailForm = ref({
 | 
				
			||||||
const writableFields: Array<string> = []; // 表单可以编辑的字段
 | 
					const writableFields: Array<string> = []; // 表单可以编辑的字段
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 加载流程实例 */
 | 
					/** 加载流程实例 */
 | 
				
			||||||
const BusinessFormComponent = ref<any>(null); // 异步组件
 | 
					const BusinessFormComponent = shallowRef<any>(null); // 异步组件
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 获取详情 */
 | 
					/** 获取详情 */
 | 
				
			||||||
async function getDetail() {
 | 
					async function getDetail() {
 | 
				
			||||||
| 
						 | 
					@ -221,6 +222,20 @@ const setFieldPermission = (field: string, permission: string) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** 当前的Tab */
 | 
					/** 当前的Tab */
 | 
				
			||||||
const activeTab = ref('form');
 | 
					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[]>([]); // 用户列表
 | 
					const userOptions = ref<SystemUserApi.User[]>([]); // 用户列表
 | 
				
			||||||
| 
						 | 
					@ -332,7 +347,13 @@ onMounted(async () => {
 | 
				
			||||||
            </TabPane>
 | 
					            </TabPane>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TabPane tab="流转记录" key="record">
 | 
					            <TabPane tab="流转记录" key="record">
 | 
				
			||||||
              <div>待开发</div>
 | 
					              <div class="h-full">
 | 
				
			||||||
 | 
					                <BpmProcessInstanceTaskList
 | 
				
			||||||
 | 
					                  ref="taskListRef"
 | 
				
			||||||
 | 
					                  :loading="processInstanceLoading"
 | 
				
			||||||
 | 
					                  :id="id"
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
            </TabPane>
 | 
					            </TabPane>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- TODO 待开发 -->
 | 
					            <!-- 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