feat(ui): redirect tenant switch away from tenant-scoped routes
Switching the active tenant triggers window.location.reload() to re-key every cached store / open SSE stream / in-flight request. The catch: when the user is currently sitting on a route whose URL embeds a tenant-scoped resource id, the new tenant lands on a 404/403 and the user sees a blank screen. Three risky route shapes today: /platform/knowledge-bases/:kbId /platform/knowledge-bases/:kbId/creatChat /platform/chat/:chatid Add a tiny tenantSwitch utility (navigateAfterTenantSwitch) that matches the current path against this list and redirects to /platform/knowledge-bases — the platform default landing, always renderable regardless of role. Anything else (settings, agent list, KB list, etc.) continues to reload its current path because those views handle empty/forbidden data gracefully. Both tenant-switch call sites (TenantSelector and UserMenu) use the helper so the matrix lives in one place; future tenant-scoped routes only need to add a regex to the list.
This commit is contained in:
@@ -82,6 +82,7 @@ import { useAuthStore } from '@/stores/auth'
|
||||
import { searchTenants, type TenantInfo } from '@/api/tenant'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { MessagePlugin } from 'tdesign-vue-next'
|
||||
import { navigateAfterTenantSwitch } from '@/utils/tenantSwitch'
|
||||
|
||||
const { t } = useI18n()
|
||||
const authStore = useAuthStore()
|
||||
@@ -161,7 +162,7 @@ const clearSearch = () => {
|
||||
const selectTenant = (tenantId: number) => {
|
||||
// 找到选中的租户信息
|
||||
const selectedTenant = tenants.value.find(t => t.id === tenantId)
|
||||
|
||||
|
||||
if (tenantId === defaultTenantId.value) {
|
||||
authStore.setSelectedTenant(null, null)
|
||||
} else {
|
||||
@@ -169,8 +170,9 @@ const selectTenant = (tenantId: number) => {
|
||||
}
|
||||
closeDropdown()
|
||||
MessagePlugin.success(t('tenant.switchSuccess'))
|
||||
// 切换租户后跳转到新租户下安全的入口(详见 tenantSwitch.ts 注释)。
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
navigateAfterTenantSwitch()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
|
||||
@@ -214,6 +214,7 @@ import { getCurrentUser, logout as logoutApi } from '@/api/auth'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import IMChannelsOverviewPanel from '@/components/IMChannelsOverviewPanel.vue'
|
||||
import { listAllIMChannels, type IMChannelOverview } from '@/api/agent'
|
||||
import { navigateAfterTenantSwitch } from '@/utils/tenantSwitch'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -372,10 +373,12 @@ const switchToTenant = (m: Membership) => {
|
||||
closeAll()
|
||||
MessagePlugin.success(t('tenant.switchSuccess'))
|
||||
// Hard reload so every cached store / open SSE stream / in-flight
|
||||
// request gets re-keyed under the new tenant. Same approach as
|
||||
// TenantSelector.vue.
|
||||
// request gets re-keyed under the new tenant. If the current path
|
||||
// embeds a tenant-scoped resource id, reload would white-screen the
|
||||
// user; navigateAfterTenantSwitch redirects to the platform home in
|
||||
// that case. Same helper as TenantSelector.vue.
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
navigateAfterTenantSwitch()
|
||||
}, 400)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Tenant-switch navigation helper.
|
||||
//
|
||||
// Switching the active tenant triggers a full page reload so every cached
|
||||
// store / open SSE stream / in-flight request gets re-keyed under the new
|
||||
// tenant. The catch: if the user is currently sitting on a route whose URL
|
||||
// embeds a tenant-scoped resource id (KB id, session id), reloading lands
|
||||
// them on a 403/empty page in the new tenant — visible to the user as a
|
||||
// blank screen. For those routes we redirect to the platform default
|
||||
// landing page instead, which is always safe regardless of role.
|
||||
//
|
||||
// Anything not on this list keeps its current path on reload (settings,
|
||||
// agent list, KB list, etc. — they re-fetch under the new tenant
|
||||
// naturally).
|
||||
|
||||
const TENANT_SCOPED_ROUTE_PATTERNS: RegExp[] = [
|
||||
/^\/platform\/knowledge-bases\/[^/]+$/, // /platform/knowledge-bases/:kbId
|
||||
/^\/platform\/knowledge-bases\/[^/]+\/creatChat$/, // /platform/knowledge-bases/:kbId/creatChat
|
||||
/^\/platform\/chat\/[^/]+$/, // /platform/chat/:chatid
|
||||
]
|
||||
|
||||
const SAFE_FALLBACK_PATH = '/platform/knowledge-bases'
|
||||
|
||||
/**
|
||||
* Return the URL to navigate to after a tenant switch. If the current path
|
||||
* embeds a tenant-scoped id, returns the safe fallback; otherwise returns
|
||||
* null to signal "reload current path".
|
||||
*/
|
||||
export function tenantSwitchTargetPath(currentPath: string): string | null {
|
||||
const isScoped = TENANT_SCOPED_ROUTE_PATTERNS.some(re => re.test(currentPath))
|
||||
return isScoped ? SAFE_FALLBACK_PATH : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the post-switch navigation. Either redirect to the safe fallback
|
||||
* (if the current page is tenant-scoped) or hard-reload the current page.
|
||||
*/
|
||||
export function navigateAfterTenantSwitch(): void {
|
||||
const target = tenantSwitchTargetPath(window.location.pathname)
|
||||
if (target !== null) {
|
||||
window.location.href = target
|
||||
} else {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user