Files
flow-center-front/src/App.vue
T

147 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch, onBeforeUnmount } from "vue";
import layout from "./components/layout.vue";
import { useRouter } from "vue-router";
import config from "@/assets/config/host.config";
import { baseApi } from "@/api/module/base";
import { resolveMenuPage } from "@/utils/menuRoute";
import { isBareTaskRoute } from "@/utils/taskEmbed";
import { Message } from "view-ui-plus";
const router = useRouter();
const isLogin = ref(false);
// 嵌入页旁路(/embed/**,iframe 免签嵌入):子应用 Drawer 里打开,带不了本应用 CAS 会话,
// 也不该出现菜单壳。判定用路由前缀而非 query 标记——刷新/直链/分享均稳定
const isTokenEmbed = computed(() => router.currentRoute.value.path.startsWith("/embed/"));
// 去壳任务页(/task/{taskId}?embed=1,v2.1 完整办理内嵌):只去菜单壳,CAS 一律保留——
// 办理是写操作,旁路鉴权等于把审批动作挂在免签 URL 上。与 token 页共享"无壳"渲染,
// 但鉴权分支只走 isTokenEmbed
const isBareTask = computed(() =>
isBareTaskRoute(router.currentRoute.value.path, router.currentRoute.value.query.embed));
const isShellless = computed(() => isTokenEmbed.value || isBareTask.value);
// 嵌入页必须旁路全局 body 的 min-width(assets/styles/base/_base.scss 的 1200px):
// 上面 isShellless 只旁路了组件壳,管不到全局 CSS,740px 的 iframe 里页面仍按 ≥1200px 排版,
// 表现为整体右偏 + 右侧被裁。用内联样式覆盖(优先级高于样式表),非嵌入态移除内联属性回落到原值
function syncEmbedLayout(embed: boolean) {
if (embed) document.body.style.minWidth = "0";
else document.body.style.removeProperty("min-width");
}
watch(isShellless, syncEmbedLayout, { immediate: true });
onBeforeUnmount(() => syncEmbedLayout(false));
const layoutData = reactive({
appName: "流程中心",
baseHost: config.baseHost,
ssoHost: config.ssoHost,
appList: [] as any[],
userInfo: null as any,
modList: [] as any[],
isAlone: false,
});
onMounted(async () => {
// 首次导航异步完成:mount 时 currentRoute 可能仍是初始 "/",先等路由就绪再判嵌入前缀,
// 否则直链 /#/embed/* 会在旁路前误发鉴权请求
await router.isReady();
// 嵌入页:跳过两个鉴权请求(失败会弹错误 Message 且渲染带菜单壳的 layout,嵌入场景均不可接受)。
// 去壳任务页不走这个分支——它是写面,getUserInfo/getUserMod 正是触发 CAS 的地方
if (isTokenEmbed.value) {
isLogin.value = true;
return;
}
try {
// 1. 获取当前用户信息
const user: any = await baseApi.getUserInfo().send();
const { id, name, profilePicture } = user || {};
// 2. 获取当前用户已授权的模块列表
const modList: any[] = await baseApi.getUserMod().send();
layoutData.userInfo = { id, name: name || "管理员", avatar: profilePicture || "" };
layoutData.modList = modList || [];
// 开发环境打印平台原始模块配置,便于排查菜单路径解析问题
if (import.meta.env.DEV) {
console.table(
(modList || []).map((m: any) => ({
name: m.name,
id: m.id,
modId: m.modId,
path: m.page,
}))
);
}
// 有子菜单时不算「单模块独占」模式
const hasChildren = (modList || []).some((m: any) => m.children && m.children.length > 0);
layoutData.isAlone = (modList || []).length <= 1 && !hasChildren;
isLogin.value = true;
// 登录后跳转第一个可用模块("/" 本身即已收到页,无需跳转)
if (router.currentRoute.value.path === "/") {
const firstModule = (modList || [])[0];
const firstRaw = firstModule?.children?.length
? firstModule.children[0]
: firstModule;
const first = resolveMenuPage(firstRaw?.page, firstRaw?.modId ?? firstRaw?.id, firstRaw?.name);
if (!first.external && first.path && first.path !== "/") {
router.replace(first.path);
}
}
} catch (error: any) {
console.error("加载用户信息失败", error);
if (error?.message !== "redirect") {
Message.error("载入用户信息失败,请检查后端服务是否正常!");
}
// 降级:使用默认值("/" 即已收到页,无需跳转)
layoutData.userInfo = { name: "管理员", avatar: "" };
layoutData.modList = [];
layoutData.isAlone = true;
isLogin.value = true;
}
});
</script>
<template>
<!-- 嵌入页:裸渲染,不套 layout 与 base-wrap-main(嵌入页自己管理样式与滚动)。
token 页维持"不等鉴权先渲染"(它本来就不发鉴权请求);去壳任务页要等 CAS 那一路走完,
否则未登录时 iframe 里先闪一个能点的办理面板 -->
<router-view v-if="isTokenEmbed" />
<router-view v-else-if="isBareTask && isLogin" />
<template v-else-if="isLogin">
<layout :layoutData="layoutData" />
<div class="base-wrap-main" :class="{ 'alone-page': layoutData.isAlone }">
<div class="main-wrapper">
<router-view />
</div>
</div>
</template>
</template>
<style lang="scss">
html,
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
#app {
height: 100%;
}
/* 页面级滚动容器:main-wrapper 只有 min-height 时内容超一屏会被 body 的
overflow:hidden 直接裁掉(如发通知页),无任何滚动条。定高 + 纵向滚动,
放得下的列表页(表格高度由 Utils.useTableHeight 算好)不受影响 */
.base-wrap-main .main-wrapper {
height: 100%;
overflow-y: auto;
}
/* 表格操作列(operatingHandle 渲染的 Tooltip 包裹层)横排且随单元格居中 */
.ivu-table-cell > .ivu-tooltip {
display: inline-block;
vertical-align: middle;
margin: 0 2px;
}
</style>