refactor: 使用映射方式替换iframe来显示流程详情
This commit is contained in:
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';
|
||||
|
||||
/**
|
||||
* 该文件存放workflow表单的iframe内嵌路由
|
||||
* 不需要权限认证 少走两个接口😅
|
||||
*/
|
||||
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,7 +35,8 @@ function formatDate(date: string) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Descriptions :column="1" size="middle">
|
||||
<div class="rounded-[6px] border p-2">
|
||||
<Descriptions v-if="data" :column="1" size="middle">
|
||||
<DescriptionsItem label="请假类型">
|
||||
{{ leaveType }}
|
||||
</DescriptionsItem>
|
||||
@ -42,4 +50,7 @@ function formatDate(date: string) {
|
||||
{{ 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>
|
||||
|
||||
21
apps/web-antd/src/views/workflow/register.ts
Normal file
21
apps/web-antd/src/views/workflow/register.ts
Normal file
@ -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…
Reference in New Issue
Block a user