Files
vue3-element-admin/src/router/guards/permission.ts
Ray.Hao d36ccf54d5 feat(form): 新增动态表单设计与数据收集功能
- src/api/form: 新增表单定义、渲染、数据收集、菜单发布接口与类型
- src/views/dynamic-form: 新增表单列表、设计器、预览、内嵌填写、公开填写、数据页
- src/plugins/form-create: 注册 form-create 渲染器、设计器与业务组件拖拽规则
- src/router: 新增公开表单分享路由 /f/:formKey,并加入登录守卫白名单
- src/enums/business: 新增 FormStatus 表单状态枚举
- src/layouts/components/LayoutSidebarItem: 支持渲染菜单角标,取自 sys_menu.params.badge
- src/main.ts: 应用启动时调用 setupFormCreate
- package.json: 新增 @form-create/element-ui 与 @form-create/designer 依赖
2026-09-01 00:17:11 +08:00

97 lines
2.7 KiB
TypeScript
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.
import type { RouteRecordRaw } from "vue-router";
import NProgress from "@/plugins/nprogress";
import router from "@/router";
import { usePermissionStore, useUserStore } from "@/stores";
import { useTenantStoreHook } from "@/stores/tenant";
import { isTenantEnabled } from "@/utils/tenant";
/**
* 路由权限守卫
*
* 处理登录验证、动态路由生成、404检测等
*/
export function setupPermissionGuard() {
// 白名单支持前缀匹配:/f 命中所有公开表单分享页 /f/:formKey
const whiteList = ["/login", "/f"];
router.beforeEach(async (to, _from) => {
NProgress.start();
try {
const isLoggedIn = useUserStore().isLoggedIn();
// 未登录处理
if (!isLoggedIn) {
const isWhiteListed = whiteList.some(
(path) => to.path === path || to.path.startsWith(`${path}/`)
);
if (isWhiteListed) {
return;
}
NProgress.done();
return `/login?redirect=${encodeURIComponent(to.fullPath)}`;
}
// 已登录访问登录页,重定向到首页
if (to.path === "/login") {
return { path: "/" };
}
const permissionStore = usePermissionStore();
const userStore = useUserStore();
// 动态路由生成
if (!permissionStore.isRouteGenerated) {
if (!userStore.userInfo?.roles?.length) {
await userStore.getUserInfo();
}
// 加载用户租户列表(VITE_APP_TENANT_ENABLED=true 时生效)
await initTenantContext();
const dynamicRoutes = await permissionStore.generateRoutes();
dynamicRoutes.forEach((route: RouteRecordRaw) => {
router.addRoute(route);
});
return { ...to, replace: true };
}
// 路由 404 检查
if (to.matched.length === 0) {
// 从登录页跳转且目标路径无效,回退首页(避免不同用户权限不同导致的 404)
if (_from.path === "/login") {
return { path: "/", replace: true };
}
return "/404";
}
// 动态标题
const title = (to.params.title as string) || (to.query.title as string);
if (title) {
to.meta.title = title;
}
} catch (error) {
console.error("Route guard error:", error);
await useUserStore().resetAllState();
NProgress.done();
return "/login";
}
});
router.afterEach(() => {
NProgress.done();
});
}
/** 初始化多租户上下文,未启用或失败时静默跳过 */
async function initTenantContext(): Promise<void> {
if (!isTenantEnabled()) return;
try {
await useTenantStoreHook().loadTenant();
} catch {
// 静默失败,不影响主流程
}
}