refactor: 使用映射方式替换iframe来显示流程详情

master
dap 5 months ago
parent 46e642a2ce
commit c401a3092f

@ -21,7 +21,7 @@ export interface TaskInfo {
permissionList?: any;
userList?: any;
formCustom: string;
formPath?: any;
formPath: string;
flowCode: string;
version: string;
flowStatus: string;

@ -3,7 +3,6 @@ import type { RouteRecordRaw } from 'vue-router';
import { mergeRouteModules, traverseTreeValues } from '@vben/utils';
import { coreRoutes, fallbackNotFoundRoute } from './core';
import { workflowIframeRoutes } from './workflow-iframe';
const dynamicRouteFiles = import.meta.glob('./modules/**/*.ts', {
eager: true,
@ -27,12 +26,11 @@ const externalRoutes: RouteRecordRaw[] = [];
const routes: RouteRecordRaw[] = [
...coreRoutes,
...externalRoutes,
...workflowIframeRoutes,
fallbackNotFoundRoute,
];
/** 基本路由(登录, 第三方登录, 注册等) + workflowIframe路由不需要拦截 */
const basicRoutes = [...coreRoutes, ...workflowIframeRoutes];
/** 基本路由(登录, 第三方登录, 注册等) */
const basicRoutes = [...coreRoutes];
/** 基本路由列表,这些路由不需要进入权限拦截 */
const coreRouteNames = traverseTreeValues(basicRoutes, (route) => route.name);

@ -1,18 +0,0 @@
import type { RouteRecordRaw } from '@vben/types';
/**
* workflowiframe
* 😅
*/
export const workflowIframeRoutes: RouteRecordRaw[] = [
// 这里是iframe使用的 去掉外层的BasicLayout
{
name: 'WorkflowLeaveInner',
path: '/workflow/leaveEdit/index/iframe',
component: () => import('#/views/workflow/leave/leave-form.vue'),
meta: {
hideInTab: true,
title: '请假申请',
},
},
];

@ -1,16 +1,17 @@
<!--
审批详情
约定${task.formPath}/frame 为内嵌表单 用于展示 需要在本地路由添加
apps/web-antd/src/router/routes/workflow-iframe.ts
动态渲染要显示的内容 需要再flowDescripionsMap先定义好组件
-->
<script setup lang="ts">
import type { DescripionsMapKey } from '../register';
import type { FlowInfoResponse } from '#/api/workflow/instance/model';
import type { TaskInfo } from '#/api/workflow/task/model';
import { Divider, Skeleton } from 'ant-design-vue';
import { Divider } from 'ant-design-vue';
import { ApprovalTimeline } from '.';
import { flowDescripionsMap } from '../register';
defineOptions({
name: 'ApprovalDetails',
@ -27,14 +28,14 @@ defineProps<{
<template>
<div>
<!-- 约定${task.formPath}/frame 为内嵌表单 用于展示 需要在本地路由添加 -->
<iframe
v-show="iframeLoaded"
:src="`${task.formPath}/iframe?readonly=true&id=${task.businessId}`"
:style="{ height: `${iframeHeight}px` }"
class="w-full"
></iframe>
<Skeleton v-show="!iframeLoaded" :paragraph="{ rows: 6 }" active />
<!--
动态渲染要显示的内容 需要再flowDescripionsMap先定义好组件
business-id为业务ID 必传
-->
<component
:is="flowDescripionsMap[task.formPath as DescripionsMapKey]"
:business-id="task.businessId"
/>
<Divider />
<ApprovalTimeline :list="currentFlowInfo.list" />
</div>

@ -96,9 +96,7 @@ export const columns: VxeGridProps['columns'] = [
},
];
export const modalSchema: (isEdit: boolean) => VbenFormSchema[] = (
isEdit: boolean,
) => [
export const modalSchema: () => VbenFormSchema[] = () => [
{
label: '主键',
fieldName: 'id',
@ -120,7 +118,6 @@ export const modalSchema: (isEdit: boolean) => VbenFormSchema[] = (
defaultValue: 'leave1',
rules: 'selectRequired',
dependencies: {
show: () => isEdit,
triggerFields: [''],
},
},

@ -1,11 +1,12 @@
<script setup lang="ts">
import type { LeaveVO } from './api/model';
import type { LeaveVO } from '../leave/api/model';
import { computed } from 'vue';
import { computed, onMounted, shallowRef } from 'vue';
import { Descriptions, DescriptionsItem } from 'ant-design-vue';
import { Descriptions, DescriptionsItem, Skeleton } from 'ant-design-vue';
import dayjs from 'dayjs';
import { leaveInfo } from './api';
import { leaveTypeOptions } from './data';
defineOptions({
@ -13,11 +14,17 @@ defineOptions({
inheritAttrs: false,
});
const props = defineProps<{ data: LeaveVO }>();
const props = defineProps<{ businessId: number | string }>();
const data = shallowRef<LeaveVO>();
onMounted(async () => {
const resp = await leaveInfo(props.businessId);
data.value = resp;
});
const leaveType = computed(() => {
return (
leaveTypeOptions.find((item) => item.value === props.data.leaveType)
leaveTypeOptions.find((item) => item.value === data.value?.leaveType)
?.label ?? '未知'
);
});
@ -28,18 +35,22 @@ function formatDate(date: string) {
</script>
<template>
<Descriptions :column="1" size="middle">
<DescriptionsItem label="请假类型">
{{ leaveType }}
</DescriptionsItem>
<DescriptionsItem label="请假时间">
{{ formatDate(data.startDate) }} - {{ formatDate(data.endDate) }}
</DescriptionsItem>
<DescriptionsItem label="请假时长">
{{ data.leaveDays }}
</DescriptionsItem>
<DescriptionsItem label="请假原因">
{{ data.remark || '无' }}
</DescriptionsItem>
</Descriptions>
<div class="rounded-[6px] border p-2">
<Descriptions v-if="data" :column="1" size="middle">
<DescriptionsItem label="请假类型">
{{ leaveType }}
</DescriptionsItem>
<DescriptionsItem label="请假时间">
{{ formatDate(data.startDate) }} - {{ formatDate(data.endDate) }}
</DescriptionsItem>
<DescriptionsItem label="请假时长">
{{ data.leaveDays }}
</DescriptionsItem>
<DescriptionsItem label="请假原因">
{{ data.remark || '无' }}
</DescriptionsItem>
</Descriptions>
<Skeleton active v-else />
</div>
</template>

@ -1,9 +1,7 @@
<script setup lang="ts">
import type { LeaveVO } from './api/model';
import type { StartWorkFlowReqData } from '#/api/workflow/task/model';
import { computed, onMounted, ref, useTemplateRef } from 'vue';
import { onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useVbenModal } from '@vben/common-ui';
@ -18,19 +16,10 @@ import { startWorkFlow } from '#/api/workflow/task';
import { applyModal } from '../components';
import { leaveAdd, leaveInfo, leaveUpdate } from './api';
import { modalSchema } from './data';
import LeaveDescription from './leave-description.vue';
const route = useRoute();
const readonly = route.query?.readonly === 'true';
const id = route.query?.id as string;
/**
* id存在&readonly时候
*/
const showActionBtn = computed(() => {
return !readonly;
});
const [BasicForm, formApi] = useVbenForm({
commonConfig: {
//
@ -40,45 +29,20 @@ const [BasicForm, formApi] = useVbenForm({
//
componentProps: {
class: 'w-full',
disabled: readonly,
},
},
schema: modalSchema(!readonly),
schema: modalSchema(),
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
const leaveDescription = ref<LeaveVO>();
const showDescription = computed(() => {
return readonly && leaveDescription.value;
});
const cardRef = useTemplateRef('cardRef');
onMounted(async () => {
//
if (id) {
const resp = await leaveInfo(id);
leaveDescription.value = resp;
await formApi.setValues(resp);
const dateRange = [dayjs(resp.startDate), dayjs(resp.endDate)];
await formApi.setFieldValue('dateRange', dateRange);
/**
* window.parent最近的上一级父页面
* 主要解决内嵌iframe卡顿的问题
*/
if (readonly) {
//
window.parent.postMessage({ type: 'mounted' }, '*');
//
setTimeout(() => {
const el = cardRef.value?.$el as HTMLDivElement;
//
const height = el?.offsetHeight ?? 0;
if (height) {
window.parent.postMessage({ type: 'height', height }, '*');
}
});
}
}
});
@ -156,22 +120,14 @@ function handleComplete() {
formApi.resetForm();
router.push('/demo/leave');
}
/**
* 显示详情时 需要较小的padding
*/
const cardSize = computed(() => {
return showDescription.value ? 'small' : 'default';
});
</script>
<template>
<Card ref="cardRef" :size="cardSize">
<Card>
<div id="leave-form">
<!-- 使用v-if会影响生命周期 -->
<BasicForm v-show="!showDescription" />
<LeaveDescription v-if="showDescription" :data="leaveDescription!" />
<div v-if="showActionBtn" class="flex justify-end gap-2">
<BasicForm />
<div class="flex justify-end gap-2">
<a-button @click="handleTempSave"></a-button>
<a-button type="primary" @click="handleStartWorkFlow"></a-button>
</div>
@ -179,14 +135,3 @@ const cardSize = computed(() => {
</div>
</Card>
</template>
<style lang="scss">
html:has(#leave-form) {
/**
去除顶部进度条样式
*/
#nprogress {
display: none;
}
}
</style>

@ -0,0 +1,21 @@
import { defineAsyncComponent, markRaw } from 'vue';
/**
*
*/
const LeaveDescription = defineAsyncComponent(
() => import('#/views/workflow/leave/leave-description.vue'),
);
/**
* key(task.formPath) value
*/
export const flowDescripionsMap = {
/**
*
*/
'/workflow/leaveEdit/index': markRaw(LeaveDescription),
};
export type DescripionsMapKey = keyof typeof flowDescripionsMap;
Loading…
Cancel
Save