This commit is contained in:
2026-04-25 11:59:14 +08:00
parent a7cd16848c
commit 138a0dc288
26 changed files with 1665 additions and 215 deletions

View File

@@ -1,12 +1,13 @@
import { useUserStore } from '@/stores/user'
import { createRouter, createWebHistory } from 'vue-router'
import { isCurrentOrigin, isPortalDomainConfigReady, isSubPortal, mainPortalOrigin, subPortalOrigin } from '@/constants/portal'
import { statisticsRoutes } from './modules/statistics'
// 路由配置
const routes = [
{
path: '/',
redirect: '/dashboard'
redirect: () => (isSubPortal ? '/sub/auth/login' : '/dashboard')
},
{
path: '/auth',
@@ -51,6 +52,44 @@ const routes = [
}
]
},
{
path: '/sub/auth',
component: () => import('@/layouts/AuthLayout.vue'),
meta: { requiresAuth: false },
children: [
{
path: 'login',
name: 'SubLogin',
component: () => import('@/pages/sub-portal/SubLogin.vue'),
meta: { title: '子账号登录' }
},
{
path: 'register',
name: 'SubRegister',
component: () => import('@/pages/sub-portal/SubRegister.vue'),
meta: { title: '子账号注册' }
},
{
path: 'reset',
name: 'SubResetPassword',
component: () => import('@/pages/auth/ResetPassword.vue'),
meta: { title: '重置密码' }
}
]
},
{
path: '/parent/subordinates',
component: () => import('@/layouts/MainLayout.vue'),
meta: { requiresAuth: true, title: '下属' },
children: [
{
path: '',
name: 'ParentSubordinates',
component: () => import('@/pages/parent/SubordinateManagement.vue'),
meta: { title: '下属' }
}
]
},
{
path: '/products',
component: () => import('@/layouts/MainLayout.vue'),
@@ -344,9 +383,34 @@ const router = createRouter({
// 路由守卫
router.beforeEach(async (to, from, next) => {
const userStore = useUserStore()
const loginPathByRoute = to.path.startsWith('/sub/') ? '/sub/auth/login' : '/auth/login'
const isSubAuthRoute = to.path.startsWith('/sub/auth')
const isMainAuthRoute = to.path.startsWith('/auth')
const subAuthToMainAuthPath = to.path.replace('/sub/auth', '/auth')
const mainAuthToSubAuthPath = to.path.replace('/auth', '/sub/auth')
// 域名级认证路由隔离:子域只允许 /sub/auth/*,主域禁止 /sub/auth/*
if (isPortalDomainConfigReady) {
const onSubDomain = isCurrentOrigin(subPortalOrigin)
const onMainDomain = isCurrentOrigin(mainPortalOrigin)
if (onSubDomain && isMainAuthRoute) {
next(mainAuthToSubAuthPath)
return
}
if (onMainDomain && isSubAuthRoute) {
next(subAuthToMainAuthPath)
return
}
} else if (isSubPortal && isMainAuthRoute) {
// 子站壳模式下,即使未配置双域名,也只允许进入 /sub/auth/*
next(mainAuthToSubAuthPath)
return
}
// 对于不需要认证的路由(如登录页),不等待初始化,直接放行
const isAuthRoute = to.path.startsWith('/auth')
const isAuthRoute = to.path.startsWith('/auth') || to.path.startsWith('/sub/auth')
const requiresAuth = to.meta.requiresAuth
// 只有在需要认证的路由上才等待初始化
@@ -366,7 +430,7 @@ router.beforeEach(async (to, from, next) => {
// 检查是否需要认证
if (requiresAuth && !userStore.isLoggedIn) {
next('/auth/login')
next(loginPathByRoute)
return
}
@@ -376,12 +440,57 @@ router.beforeEach(async (to, from, next) => {
return
}
// 登录用户访问认证页面,重定向到数据大厅
if (isAuthRoute && userStore.isLoggedIn) {
// 登录态下强制要求主/子域配置完整,避免混域运行
if (userStore.isLoggedIn && !isPortalDomainConfigReady) {
if (import.meta.env.PROD) {
userStore.logout()
next(loginPathByRoute)
return
}
}
// 域名隔离:子账号登录态必须在子账号专属域名
if (userStore.isLoggedIn && userStore.accountKind === 'subordinate' && subPortalOrigin && !isCurrentOrigin(subPortalOrigin)) {
window.location.replace(`${subPortalOrigin}${to.fullPath}`)
return
}
// 域名隔离:普通/管理员账号不应停留在子账号专属域名
if (userStore.isLoggedIn && userStore.accountKind !== 'subordinate' && subPortalOrigin && isCurrentOrigin(subPortalOrigin)) {
if (mainPortalOrigin) {
window.location.replace(`${mainPortalOrigin}${to.fullPath}`)
return
}
next('/products')
return
}
// 已登录用户访问认证页面,按账号类型重定向到对应首页
if (isAuthRoute && userStore.isLoggedIn) {
next(userStore.accountKind === 'subordinate' ? '/subscriptions' : '/dashboard')
return
}
// 子站壳:禁止进入主站登录/注册路由(可合并子账号入口到 /sub/auth
if (isSubPortal && (to.path.startsWith('/auth/login') || to.path.startsWith('/auth/register'))) {
next(to.path.replace('/auth/', '/sub/auth/'))
return
}
// 主站与「子站壳」共仓:/sub/auth/* 始终可用,子账号注册/登录与主站 /auth 并存,邀请链接不依赖单独构建
// 下属账号不可进入主账号「下属」管理页
if (to.path.startsWith('/parent/subordinates') && userStore.accountKind === 'subordinate') {
next('/subscriptions')
return
}
// 下属账号不允许访问仪表盘
if (to.path.startsWith('/dashboard') && userStore.accountKind === 'subordinate') {
next('/subscriptions')
return
}
next()
})