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:
@@ -1,28 +1,5 @@
|
||||
<template>
|
||||
<div class="puro-page">
|
||||
<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>
|
||||
|
||||
<div>
|
||||
<section class="docs-hero container">
|
||||
<h1>{{ $t('docs.hero.title') }}</h1>
|
||||
<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">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import PuroLocaleSwitcher from '@/components/puro/PuroLocaleSwitcher.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
||||
@@ -1,30 +1,5 @@
|
||||
<template>
|
||||
<div class="puro-page">
|
||||
<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>
|
||||
|
||||
<div>
|
||||
<!-- HERO -->
|
||||
<section class="hero container">
|
||||
<div class="hero-eyebrow">
|
||||
@@ -304,45 +279,10 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
|
||||
</div>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import PuroLocaleSwitcher from '@/components/puro/PuroLocaleSwitcher.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,31 +1,5 @@
|
||||
<template>
|
||||
<div class="puro-page">
|
||||
<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>
|
||||
|
||||
<div>
|
||||
<!-- HERO -->
|
||||
<section class="hero">
|
||||
<div class="section-kicker" style="margin-bottom:14px;">{{ $t('pricing.hero.kicker') }}</div>
|
||||
@@ -362,7 +336,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import PuroLocaleSwitcher from '@/components/puro/PuroLocaleSwitcher.vue'
|
||||
import PricingCalculator from '@/components/puro/PricingCalculator.vue'
|
||||
|
||||
const customAmt = ref(50)
|
||||
|
||||
Reference in New Issue
Block a user