import auth, { AuthParams } from '@/utils/authentication'; import { useEffect, useMemo, useState } from 'react'; export type IRoute = AuthParams & { name: string; key: string; // 当前页是否展示面包屑 breadcrumb?: boolean; children?: IRoute[]; // 当前路由是否渲染菜单项,为 true 的话不会在菜单中显示,但可通过路由地址访问。 ignore?: boolean; // 是否仅平台超级管理员可见 superAdminOnly?: boolean; // 是否仅租户可见 tenantOnly?: boolean; // 角色白名单访问限制 roles?: string[]; }; export interface RoleContext { isSuper?: boolean; role?: string; tenantId?: string | number; } export const routes: IRoute[] = [ { name: 'menu.dashboard', key: 'dashboard', children: [ { name: 'menu.dashboard.workplace', key: 'dashboard/workplace', }, { name: 'menu.dashboard.radarMap', key: 'dashboard/radar-map', }, { name: 'menu.dashboard.financial', key: 'dashboard/financial', superAdminOnly: true, }, ], }, { name: 'menu.card', key: 'card', children: [ { name: 'menu.card.list', key: 'card/list' }, { name: 'menu.card.builder', key: 'card/builder' }, { name: 'menu.card.cases', key: 'card/cases' }, { name: 'menu.card.articles', key: 'card/articles' }, ], }, { name: 'menu.crm', key: 'crm', children: [ { name: 'menu.crm.leads', key: 'crm/leads' }, { name: 'menu.crm.pipeline', key: 'crm/pipeline' }, { name: 'menu.crm.radarLog', key: 'crm/radar-log' }, { name: 'menu.crm.leadProfile', key: 'crm/lead-profile', ignore: true }, ], }, { name: 'menu.nfc', key: 'nfc', children: [ { name: 'menu.nfc.devices', key: 'nfc/devices' }, { name: 'menu.nfc.batch', key: 'nfc/batch' }, { name: 'menu.nfc.rules', key: 'nfc/rules' }, ], }, { name: 'menu.ai', key: 'ai', children: [ { name: 'menu.ai.knowledge', key: 'ai/knowledge' }, { name: 'menu.ai.concierge', key: 'ai/concierge' }, { name: 'menu.ai.translation', key: 'ai/translation' }, ], }, { name: 'menu.team', key: 'team', children: [ { name: 'menu.team.members', key: 'team/members' }, { name: 'menu.team.brandKit', key: 'team/brand-kit', roles: ['super_admin', 'tenant_owner', 'admin'], }, { name: 'menu.team.handover', key: 'team/handover', roles: ['super_admin', 'tenant_owner', 'admin'], }, ], }, { name: 'menu.settingsBiz', key: 'settings-biz', children: [ { name: 'menu.settings.tenant', key: 'settings/tenant', roles: ['super_admin', 'tenant_owner', 'admin'], }, { name: 'menu.settings.billing', key: 'settings/billing', roles: ['super_admin', 'tenant_owner', 'admin'], }, { name: 'menu.settings.auditLogs', key: 'settings/audit-logs' }, ], }, { name: 'menu.platform', key: 'platform', superAdminOnly: true, children: [ { name: 'menu.platform.subscribers', key: 'platform/subscribers' }, { name: 'menu.platform.renewals', key: 'platform/renewals' }, { name: 'menu.platform.subscriptions', key: 'platform/subscriptions' }, ], }, { name: 'menu.exception', key: 'exception', ignore: true, children: [ { name: 'menu.exception.403', key: 'exception/403', }, { name: 'menu.exception.404', key: 'exception/404', }, { name: 'menu.exception.500', key: 'exception/500', }, ], }, ]; export const getName = (path: string, routes) => { return routes.find((item) => { const itemPath = `/${item.key}`; if (path === itemPath) { return item.name; } else if (item.children) { return getName(path, item.children); } }); }; export const filterRoutesByRole = ( routesList: IRoute[], roleCtx?: RoleContext ): IRoute[] => { if (!routesList || !routesList.length) { return []; } const isSuper = Boolean(roleCtx?.isSuper || roleCtx?.role === 'super_admin'); const role = roleCtx?.role || (isSuper ? 'super_admin' : 'sales_rep'); const result: IRoute[] = []; for (const route of routesList) { // 1. 平台超级管理员专属路由隔离 if (route.superAdminOnly && !isSuper) { continue; } // 2. 租户专属路由隔离 if (route.tenantOnly && isSuper) { continue; } // 3. 角色白名单访问限制 if (route.roles && route.roles.length > 0) { const isAllowed = isSuper || route.roles.includes(role); if (!isAllowed) { continue; } } // 4. 递归处理子路由 if (route.children && route.children.length > 0) { const filteredChildren = filterRoutesByRole(route.children, roleCtx); if (filteredChildren.length === 0) { continue; } result.push({ ...route, children: filteredChildren, }); } else { result.push({ ...route }); } } return result; }; export const generatePermission = (role: string) => { const actions = role === 'admin' || role === 'super_admin' ? ['*'] : ['read']; const result = {}; routes.forEach((item) => { if (item.children) { item.children.forEach((child) => { result[child.name] = actions; }); } }); return result; }; const useRoute = ( userPermission?: Record, roleCtx?: RoleContext ): [IRoute[], string] => { const filterRoute = (routes: IRoute[], arr = []): IRoute[] => { if (!routes.length) { return []; } for (const route of routes) { const { requiredPermissions, oneOfPerm } = route; let visible = true; if (requiredPermissions && userPermission) { visible = auth({ requiredPermissions, oneOfPerm }, userPermission); } if (!visible) { continue; } if (route.children && route.children.length) { const newRoute = { ...route, children: [] }; filterRoute(route.children, newRoute.children); if (newRoute.children.length) { arr.push(newRoute); } } else { arr.push({ ...route }); } } return arr; }; const [permissionRoute, setPermissionRoute] = useState(routes); useEffect(() => { const roleFiltered = filterRoutesByRole(routes, roleCtx); const newRoutes = filterRoute(roleFiltered); setPermissionRoute(newRoutes); }, [JSON.stringify(userPermission), JSON.stringify(roleCtx)]); const defaultRoute = useMemo(() => { const first = permissionRoute[0]; if (first) { const firstRoute = first?.children?.[0]?.key || first.key; return firstRoute; } return ''; }, [permissionRoute]); return [permissionRoute, defaultRoute]; }; export default useRoute;