Vue 3多租户系统需解耦登录、租户、身份:token存localStorage/Pinia persist用于鉴权,tenantId存sessionStorage/Pinia state避免跨页污染;路由菜单按tenantId动态加载,API请求通过拦截器自动注入X-Tenant-ID,切换租户时清缓存但保留token,用户面板提供显式切换入口。

Vue 3 中实现多租户系统的组织架构切换,核心不是“登录一次就完事”,而是把登录状态、租户上下文、用户身份三者解耦管理。登录只负责认证,租户切换是独立的状态操作,不能混在 login() 里硬编码。
租户信息必须独立于用户凭证存储
用户可能属于多个租户(比如小张同时是A公司HR和B公司顾问),所以 userInfo.token 和当前激活的 tenantId 必须分开存、分开用:
- token 存 localStorage 或 Pinia persist(仅限 token 字段)——用于所有接口鉴权
- tenantId 存 sessionStorage 或 Pinia 普通 state ——避免跨标签页污染,切换后仅影响当前会话
- 租户元数据(名称、logo、权限范围等)建议按需拉取,不随登录全量返回,减少首屏压力
路由与菜单需支持租户级动态渲染
侧边栏菜单、顶部导航、甚至页面标题,都应根据当前 tenantId 重新计算,而不是复用全局静态路由:
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 在路由守卫中拦截 /home 等入口路径,检查是否有有效 tenantId;若无,跳转到租户选择页(如 /tenant/select)
- 菜单数据不再写死在 router/index.ts,而是通过 useMenuStore().loadByTenant(tenantId) 异步获取
- 每个路由 meta 可扩展 tenantScopes 字段,例如 meta: { tenantScopes: ['crm', 'hrm'] },用于过滤非本租户可用模块
API 请求自动携带租户上下文
后端采用共享 Schema 或 tenant_id 字段隔离时,前端必须确保每个请求都带上租户标识,且不能依赖手动拼参:
立即学习“前端免费学习笔记(深入)”;
- 在 axios 请求拦截器中统一注入:config.headers['X-Tenant-ID'] = useTenantStore().currentTenant?.id
- 避免使用 query 参数传 tenantId(易被篡改、不安全),优先走 header 或固定前缀路径(如 /api/v1/{tenantId}/users)
- 登出或切换租户时,清空当前租户缓存(如菜单、字典项),但保留 token,避免重复登录
用户中心提供显式租户切换入口
租户切换不是隐藏功能,而应作为一级用户操作暴露:
- 点击头像弹出的用户面板里,列出该用户所属的所有租户(从 /user/tenants 接口获取)
- 高亮当前租户,支持一键切换:useTenantStore().switchTo(tenant) → 触发菜单重载 + 请求头更新 + 路由刷新
- 切换后保留原页面路径(如从 /crm/contacts 切换,仍回到 /crm/contacts),仅刷新数据和权限视图

















