refactor(portal): extract PortalLayout so Nav/Footer persist across routes
Some checks failed
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Security Scan / backend-security (push) Has been cancelled
Security Scan / frontend-security (push) Has been cancelled

将 Landing/Docs/Pricing 的 Nav + Footer 提取到共享 PortalLayout。
Router 改为嵌套结构,路由切换时 router-view 内容变化,Nav 本身
不重挂载,消除切页时的 UI 抖动(真·SPA 行为)。

- new: components/layout/PortalLayout.vue(Nav + router-view + Footer)
- router: /、/docs、/pricing 作为 PortalLayout 的子路由
- i18n: 新增 portal.nav.* 命名空间;删除重复的 docs.nav.* / pricing.nav.* / landing.nav.*
- router: scrollBehavior 支持 hash 锚点跳转(offset 80px 绕开 sticky nav)
- router-link 使用 active-class/exact-active-class prop 替代硬编码 class="active"
This commit is contained in:
mini
2026-04-23 12:52:07 +08:00
parent 779005e1cd
commit e7f3fe5b4d
7 changed files with 174 additions and 168 deletions

View File

@@ -120,32 +120,38 @@ const routes: RouteRecordRaw[] = [
title: 'Key Usage',
}
},
{
path: '/docs',
name: 'Docs',
component: () => import('@/views/docs/DocsView.vue'),
meta: {
requiresAuth: false,
title: 'PURO AI · 文档'
}
},
{
path: '/pricing',
name: 'pricing',
component: () => import('@/views/pricing/PricingView.vue'),
meta: { requiresAuth: false, title: 'Pricing · PURO AI' }
},
// ==================== User Routes ====================
// ==================== Portal Routes (shared PortalLayout) ====================
{
path: '/',
name: 'Landing',
component: () => import('@/views/landing/LandingView.vue'),
meta: {
requiresAuth: false,
title: 'PURO AI — 你的 AI 订阅,已经付过钱了',
redirectIfAuth: '/dashboard'
}
component: () => import('@/components/layout/PortalLayout.vue'),
children: [
{
path: '',
name: 'Landing',
component: () => import('@/views/landing/LandingView.vue'),
meta: {
requiresAuth: false,
title: 'PURO AI — 你的 AI 订阅,已经付过钱了',
redirectIfAuth: '/dashboard'
}
},
{
path: 'docs',
name: 'Docs',
component: () => import('@/views/docs/DocsView.vue'),
meta: {
requiresAuth: false,
title: 'PURO AI · 文档'
}
},
{
path: 'pricing',
name: 'pricing',
component: () => import('@/views/pricing/PricingView.vue'),
meta: { requiresAuth: false, title: 'Pricing · PURO AI' }
},
]
},
{
path: '/dashboard',
@@ -521,11 +527,15 @@ const routes: RouteRecordRaw[] = [
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
scrollBehavior(_to, _from, savedPosition) {
scrollBehavior(to, _from, savedPosition) {
// Scroll to saved position when using browser back/forward
if (savedPosition) {
return savedPosition
}
// Scroll to hash target (anchor link) — offset by sticky nav height
if (to.hash) {
return { el: to.hash, behavior: 'smooth', top: 80 }
}
// Scroll to top for new routes
return { top: 0 }
}