refactor(portal): extract PortalLayout so Nav/Footer persist across routes
将 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:
129
frontend/src/components/layout/PortalLayout.vue
Normal file
129
frontend/src/components/layout/PortalLayout.vue
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<template>
|
||||||
|
<div class="puro-page">
|
||||||
|
<div class="bg-glow"></div>
|
||||||
|
<div class="grain"></div>
|
||||||
|
|
||||||
|
<nav class="nav">
|
||||||
|
<div class="container nav-inner">
|
||||||
|
<router-link to="/" class="brand">
|
||||||
|
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.8">
|
||||||
|
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" fill="rgba(34, 211, 238, 0.08)"/>
|
||||||
|
</svg>
|
||||||
|
<span>PURO AI</span>
|
||||||
|
</router-link>
|
||||||
|
<div class="nav-links">
|
||||||
|
<router-link to="/" active-class="active" exact-active-class="active">{{ $t('portal.nav.products') }}</router-link>
|
||||||
|
<router-link to="/pricing" active-class="active" exact-active-class="active">{{ $t('portal.nav.pricing') }}</router-link>
|
||||||
|
<router-link to="/docs" active-class="active" exact-active-class="active">{{ $t('portal.nav.docs') }}</router-link>
|
||||||
|
</div>
|
||||||
|
<div class="nav-cta">
|
||||||
|
<PuroLocaleSwitcher />
|
||||||
|
<router-link to="/login" class="btn btn-ghost">{{ $t('portal.nav.login') }}</router-link>
|
||||||
|
<router-link to="/register" class="btn btn-primary">{{ $t('portal.nav.signup') }}</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<router-view />
|
||||||
|
|
||||||
|
<footer class="puro-footer">
|
||||||
|
<div class="container footer-grid">
|
||||||
|
<div class="footer-brand">
|
||||||
|
<div class="brand">
|
||||||
|
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.8">
|
||||||
|
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" fill="rgba(34, 211, 238, 0.08)"/>
|
||||||
|
</svg>
|
||||||
|
<span>PURO AI</span>
|
||||||
|
</div>
|
||||||
|
<p class="footer-tagline">{{ $t('landing.footer.tagline1') }}<br>{{ $t('landing.footer.tagline2') }}</p>
|
||||||
|
<p class="footer-meta">© 2026 puro.im · MIT License<br>fork of Wei-Shaw/sub2api</p>
|
||||||
|
<div class="footer-status"><span class="dot-green"></span>all systems operational</div>
|
||||||
|
</div>
|
||||||
|
<div class="footer-col">
|
||||||
|
<div class="footer-col-title">{{ $t('landing.footer.colProducts') }}</div>
|
||||||
|
<router-link to="/docs">{{ $t('landing.footer.linkDocs') }}</router-link>
|
||||||
|
<router-link to="/pricing">{{ $t('portal.nav.pricing') }}</router-link>
|
||||||
|
<a href="https://git.puro.im/purovps/sub2api/commits/branch/main" target="_blank" rel="noopener noreferrer">{{ $t('landing.footer.linkChangelog') }}</a>
|
||||||
|
</div>
|
||||||
|
<div class="footer-col">
|
||||||
|
<div class="footer-col-title">{{ $t('landing.footer.colAccount') }}</div>
|
||||||
|
<router-link to="/login">{{ $t('landing.footer.linkLogin') }}</router-link>
|
||||||
|
<router-link to="/register">{{ $t('landing.footer.linkRegister') }}</router-link>
|
||||||
|
<router-link to="/dashboard">Dashboard</router-link>
|
||||||
|
</div>
|
||||||
|
<div class="footer-col">
|
||||||
|
<div class="footer-col-title">{{ $t('landing.footer.colContact') }}</div>
|
||||||
|
<a href="mailto:admin@puro.im">admin@puro.im</a>
|
||||||
|
<a href="https://git.puro.im" target="_blank" rel="noopener noreferrer">git.puro.im</a>
|
||||||
|
<a href="https://git.puro.im/purovps/sub2api" target="_blank" rel="noopener noreferrer">GitHub ↗</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PuroLocaleSwitcher from '@/components/puro/PuroLocaleSwitcher.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.puro-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: var(--bg-0);
|
||||||
|
color: var(--text-0);
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
position: relative;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.puro-footer {
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
padding: 48px 0 32px;
|
||||||
|
background: rgba(2, 6, 23, 0.4);
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.footer-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1.3fr 1fr 1fr 1fr;
|
||||||
|
gap: 48px;
|
||||||
|
}
|
||||||
|
@media (max-width: 720px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
|
||||||
|
.footer-brand .brand { margin-bottom: 12px; }
|
||||||
|
.footer-tagline { color: var(--text-2); font-size: 13px; line-height: 1.6; margin-bottom: 8px; max-width: 280px; }
|
||||||
|
.footer-meta { color: var(--text-3); font-size: 12px; line-height: 1.7; margin-bottom: 12px; }
|
||||||
|
.footer-status {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-2);
|
||||||
|
background: rgba(15, 23, 42, 0.6);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.dot-green {
|
||||||
|
display: inline-block;
|
||||||
|
width: 6px; height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--green, #34d399);
|
||||||
|
box-shadow: 0 0 6px rgba(52,211,153,0.6);
|
||||||
|
}
|
||||||
|
.footer-col-title {
|
||||||
|
color: var(--text-0);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.footer-col a {
|
||||||
|
display: block;
|
||||||
|
color: var(--text-2);
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.footer-col a:hover { color: var(--cyan); }
|
||||||
|
</style>
|
||||||
@@ -5630,7 +5630,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
landing: {
|
portal: {
|
||||||
nav: {
|
nav: {
|
||||||
products: 'Products',
|
products: 'Products',
|
||||||
pricing: 'Pricing',
|
pricing: 'Pricing',
|
||||||
@@ -5638,6 +5638,9 @@ export default {
|
|||||||
login: 'Sign in',
|
login: 'Sign in',
|
||||||
signup: 'Free trial →',
|
signup: 'Free trial →',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
landing: {
|
||||||
hero: {
|
hero: {
|
||||||
badgeNew: 'NEW',
|
badgeNew: 'NEW',
|
||||||
eyebrow: 'Unified access to multiple AI platforms · Zero code change',
|
eyebrow: 'Unified access to multiple AI platforms · Zero code change',
|
||||||
@@ -5714,13 +5717,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
pricing: {
|
pricing: {
|
||||||
nav: {
|
|
||||||
products: 'Products',
|
|
||||||
pricing: 'Pricing',
|
|
||||||
docs: 'Docs',
|
|
||||||
login: 'Sign in',
|
|
||||||
signup: 'Free trial →',
|
|
||||||
},
|
|
||||||
hero: {
|
hero: {
|
||||||
kicker: '// pricing · top up · pay as you go · never expires',
|
kicker: '// pricing · top up · pay as you go · never expires',
|
||||||
previewPill: '// preview · final pricing TBD at launch',
|
previewPill: '// preview · final pricing TBD at launch',
|
||||||
@@ -5917,13 +5913,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
docs: {
|
docs: {
|
||||||
nav: {
|
|
||||||
products: 'Products',
|
|
||||||
pricing: 'Pricing',
|
|
||||||
docs: 'Docs',
|
|
||||||
login: 'Sign in',
|
|
||||||
signup: 'Free trial →',
|
|
||||||
},
|
|
||||||
hero: {
|
hero: {
|
||||||
title: 'Quickstart — PURO AI',
|
title: 'Quickstart — PURO AI',
|
||||||
subtitle: 'Three steps: get a key → set base_url → send a request',
|
subtitle: 'Three steps: get a key → set base_url → send a request',
|
||||||
|
|||||||
@@ -5823,7 +5823,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
landing: {
|
portal: {
|
||||||
nav: {
|
nav: {
|
||||||
products: '产品',
|
products: '产品',
|
||||||
pricing: '定价',
|
pricing: '定价',
|
||||||
@@ -5831,6 +5831,9 @@ export default {
|
|||||||
login: '登录',
|
login: '登录',
|
||||||
signup: '免费试用 →',
|
signup: '免费试用 →',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
landing: {
|
||||||
hero: {
|
hero: {
|
||||||
badgeNew: 'NEW',
|
badgeNew: 'NEW',
|
||||||
eyebrow: '统一接入多个 AI 平台 · 零改动切换',
|
eyebrow: '统一接入多个 AI 平台 · 零改动切换',
|
||||||
@@ -5907,13 +5910,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
pricing: {
|
pricing: {
|
||||||
nav: {
|
|
||||||
products: '产品',
|
|
||||||
pricing: '定价',
|
|
||||||
docs: '文档',
|
|
||||||
login: '登录',
|
|
||||||
signup: '免费试用 →',
|
|
||||||
},
|
|
||||||
hero: {
|
hero: {
|
||||||
kicker: '// pricing · 充多少 · 用多少 · 永不过期',
|
kicker: '// pricing · 充多少 · 用多少 · 永不过期',
|
||||||
previewPill: '// preview · 最终定价以开售为准',
|
previewPill: '// preview · 最终定价以开售为准',
|
||||||
@@ -6110,13 +6106,6 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
docs: {
|
docs: {
|
||||||
nav: {
|
|
||||||
products: '产品',
|
|
||||||
pricing: '定价',
|
|
||||||
docs: '文档',
|
|
||||||
login: '登录',
|
|
||||||
signup: '免费试用 →',
|
|
||||||
},
|
|
||||||
hero: {
|
hero: {
|
||||||
title: '快速接入 PURO AI',
|
title: '快速接入 PURO AI',
|
||||||
subtitle: '三步走:拿 key → 配 base_url → 发请求',
|
subtitle: '三步走:拿 key → 配 base_url → 发请求',
|
||||||
|
|||||||
@@ -120,8 +120,24 @@ const routes: RouteRecordRaw[] = [
|
|||||||
title: 'Key Usage',
|
title: 'Key Usage',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ==================== Portal Routes (shared PortalLayout) ====================
|
||||||
{
|
{
|
||||||
path: '/docs',
|
path: '/',
|
||||||
|
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',
|
name: 'Docs',
|
||||||
component: () => import('@/views/docs/DocsView.vue'),
|
component: () => import('@/views/docs/DocsView.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
@@ -130,22 +146,12 @@ const routes: RouteRecordRaw[] = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/pricing',
|
path: 'pricing',
|
||||||
name: 'pricing',
|
name: 'pricing',
|
||||||
component: () => import('@/views/pricing/PricingView.vue'),
|
component: () => import('@/views/pricing/PricingView.vue'),
|
||||||
meta: { requiresAuth: false, title: 'Pricing · PURO AI' }
|
meta: { requiresAuth: false, title: 'Pricing · PURO AI' }
|
||||||
},
|
},
|
||||||
|
]
|
||||||
// ==================== User Routes ====================
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: 'Landing',
|
|
||||||
component: () => import('@/views/landing/LandingView.vue'),
|
|
||||||
meta: {
|
|
||||||
requiresAuth: false,
|
|
||||||
title: 'PURO AI — 你的 AI 订阅,已经付过钱了',
|
|
||||||
redirectIfAuth: '/dashboard'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dashboard',
|
path: '/dashboard',
|
||||||
@@ -521,11 +527,15 @@ const routes: RouteRecordRaw[] = [
|
|||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes,
|
routes,
|
||||||
scrollBehavior(_to, _from, savedPosition) {
|
scrollBehavior(to, _from, savedPosition) {
|
||||||
// Scroll to saved position when using browser back/forward
|
// Scroll to saved position when using browser back/forward
|
||||||
if (savedPosition) {
|
if (savedPosition) {
|
||||||
return 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
|
// Scroll to top for new routes
|
||||||
return { top: 0 }
|
return { top: 0 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="puro-page">
|
<div>
|
||||||
<div class="bg-glow soft"></div>
|
|
||||||
|
|
||||||
<nav class="nav">
|
|
||||||
<div class="container nav-inner">
|
|
||||||
<router-link to="/" class="brand">
|
|
||||||
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.8">
|
|
||||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" fill="rgba(34, 211, 238, 0.08)"/>
|
|
||||||
</svg>
|
|
||||||
<span>PURO AI</span>
|
|
||||||
</router-link>
|
|
||||||
<div class="nav-links">
|
|
||||||
<router-link to="/">{{ $t('docs.nav.products') }}</router-link>
|
|
||||||
<router-link to="/pricing">{{ $t('docs.nav.pricing') }}</router-link>
|
|
||||||
<router-link to="/docs" class="active">{{ $t('docs.nav.docs') }}</router-link>
|
|
||||||
</div>
|
|
||||||
<div class="nav-cta">
|
|
||||||
<PuroLocaleSwitcher />
|
|
||||||
<router-link to="/login" class="btn btn-ghost">{{ $t('docs.nav.login') }}</router-link>
|
|
||||||
<router-link to="/register" class="btn btn-primary">{{ $t('docs.nav.signup') }}</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<section class="docs-hero container">
|
<section class="docs-hero container">
|
||||||
<h1>{{ $t('docs.hero.title') }}</h1>
|
<h1>{{ $t('docs.hero.title') }}</h1>
|
||||||
<p class="subtitle">{{ $t('docs.hero.subtitle') }}</p>
|
<p class="subtitle">{{ $t('docs.hero.subtitle') }}</p>
|
||||||
@@ -256,7 +233,6 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import PuroLocaleSwitcher from '@/components/puro/PuroLocaleSwitcher.vue'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="puro-page">
|
<div>
|
||||||
<div class="bg-glow"></div>
|
|
||||||
<div class="grain"></div>
|
|
||||||
|
|
||||||
<!-- NAV -->
|
|
||||||
<nav class="nav">
|
|
||||||
<div class="container nav-inner">
|
|
||||||
<router-link to="/" class="brand">
|
|
||||||
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.8">
|
|
||||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" fill="rgba(34, 211, 238, 0.08)"/>
|
|
||||||
</svg>
|
|
||||||
<span>PURO AI</span>
|
|
||||||
</router-link>
|
|
||||||
<div class="nav-links">
|
|
||||||
<a href="#features">{{ $t('landing.nav.products') }}</a>
|
|
||||||
<router-link to="/pricing">{{ $t('landing.nav.pricing') }}</router-link>
|
|
||||||
<router-link to="/docs">{{ $t('landing.nav.docs') }}</router-link>
|
|
||||||
</div>
|
|
||||||
<div class="nav-cta">
|
|
||||||
<PuroLocaleSwitcher />
|
|
||||||
<router-link to="/login" class="btn btn-ghost">{{ $t('landing.nav.login') }}</router-link>
|
|
||||||
<router-link to="/register" class="btn btn-primary">{{ $t('landing.nav.signup') }}</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- HERO -->
|
<!-- HERO -->
|
||||||
<section class="hero container">
|
<section class="hero container">
|
||||||
<div class="hero-eyebrow">
|
<div class="hero-eyebrow">
|
||||||
@@ -304,45 +279,10 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ⑥ Footer -->
|
|
||||||
<footer class="puro-footer">
|
|
||||||
<div class="container footer-grid">
|
|
||||||
<div class="footer-brand">
|
|
||||||
<div class="brand">
|
|
||||||
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.8">
|
|
||||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" fill="rgba(34, 211, 238, 0.08)"/>
|
|
||||||
</svg>
|
|
||||||
<span>PURO AI</span>
|
|
||||||
</div>
|
|
||||||
<p class="footer-tagline">{{ $t('landing.footer.tagline1') }}<br>{{ $t('landing.footer.tagline2') }}</p>
|
|
||||||
<p class="footer-meta">© 2026 puro.im · MIT License<br>fork of Wei-Shaw/sub2api</p>
|
|
||||||
<div class="footer-status"><span class="dot-green"></span>all systems operational</div>
|
|
||||||
</div>
|
|
||||||
<div class="footer-col">
|
|
||||||
<div class="footer-col-title">{{ $t('landing.footer.colProducts') }}</div>
|
|
||||||
<a href="/docs">{{ $t('landing.footer.linkDocs') }}</a>
|
|
||||||
<a href="#features">{{ $t('landing.footer.linkFeatures') }}</a>
|
|
||||||
<a href="https://git.puro.im/purovps/sub2api/commits/branch/main" target="_blank" rel="noopener noreferrer">{{ $t('landing.footer.linkChangelog') }}</a>
|
|
||||||
</div>
|
|
||||||
<div class="footer-col">
|
|
||||||
<div class="footer-col-title">{{ $t('landing.footer.colAccount') }}</div>
|
|
||||||
<router-link to="/login">{{ $t('landing.footer.linkLogin') }}</router-link>
|
|
||||||
<router-link to="/register">{{ $t('landing.footer.linkRegister') }}</router-link>
|
|
||||||
<a href="/dashboard">Dashboard</a>
|
|
||||||
</div>
|
|
||||||
<div class="footer-col">
|
|
||||||
<div class="footer-col-title">{{ $t('landing.footer.colContact') }}</div>
|
|
||||||
<a href="mailto:admin@puro.im">admin@puro.im</a>
|
|
||||||
<a href="https://git.puro.im" target="_blank" rel="noopener noreferrer">git.puro.im</a>
|
|
||||||
<a href="https://git.puro.im/purovps/sub2api" target="_blank" rel="noopener noreferrer">GitHub ↗</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PuroLocaleSwitcher from '@/components/puro/PuroLocaleSwitcher.vue'
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,31 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="puro-page">
|
<div>
|
||||||
<div class="bg-glow"></div>
|
|
||||||
<div class="grain"></div>
|
|
||||||
|
|
||||||
<!-- NAV -->
|
|
||||||
<nav class="nav">
|
|
||||||
<div class="container nav-inner">
|
|
||||||
<router-link to="/" class="brand">
|
|
||||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
|
||||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
|
||||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
|
||||||
</svg>
|
|
||||||
PURO AI
|
|
||||||
</router-link>
|
|
||||||
<div class="nav-links">
|
|
||||||
<router-link to="/">{{ $t('pricing.nav.products') }}</router-link>
|
|
||||||
<router-link to="/pricing" class="active">{{ $t('pricing.nav.pricing') }}</router-link>
|
|
||||||
<router-link to="/docs">{{ $t('pricing.nav.docs') }}</router-link>
|
|
||||||
</div>
|
|
||||||
<div class="nav-cta">
|
|
||||||
<PuroLocaleSwitcher />
|
|
||||||
<router-link to="/login" class="btn btn-ghost">{{ $t('pricing.nav.login') }}</router-link>
|
|
||||||
<router-link to="/register" class="btn btn-primary">{{ $t('pricing.nav.signup') }}</router-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- HERO -->
|
<!-- HERO -->
|
||||||
<section class="hero">
|
<section class="hero">
|
||||||
<div class="section-kicker" style="margin-bottom:14px;">{{ $t('pricing.hero.kicker') }}</div>
|
<div class="section-kicker" style="margin-bottom:14px;">{{ $t('pricing.hero.kicker') }}</div>
|
||||||
@@ -362,7 +336,6 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import PuroLocaleSwitcher from '@/components/puro/PuroLocaleSwitcher.vue'
|
|
||||||
import PricingCalculator from '@/components/puro/PricingCalculator.vue'
|
import PricingCalculator from '@/components/puro/PricingCalculator.vue'
|
||||||
|
|
||||||
const customAmt = ref(50)
|
const customAmt = ref(50)
|
||||||
|
|||||||
Reference in New Issue
Block a user