Files
website/.vitepress/theme/index.ts
T
Dai0814 e4757446cf
mirror-to-gitee / mirror (push) Successful in 4s
feat(portal): 补入整站页脚,并让项目列表页不显示页脚
用户反馈 /projects/(项目列表)页底部的品牌/联系/版权区应该去掉。

背景:这几个文件此前一直未提交——远端仓库其实没有页脚,
PortalFooter.vue 只存在于本地,HEAD 版 index.ts 也没引用它。
所以本次把页脚整套补齐,再叠加「按页面决定是否展示」的逻辑。

新增/改动
- .vitepress/theme/components/PortalFooter.vue(新增,274 行)
  品牌区 + 产品体系/文档资源/关于我们/联系我们四栏 + 二维码浮层 + 版权条
- .vitepress/theme/styles/portal.css:补入全部 .pf-foot-* 样式(508 行)
  只搬运页脚相关规则,不夹带工作区里其它未提交的样式改动
- .vitepress/theme/index.ts:挂载页脚 + 页面级显隐判定
- pages/projects/index.md:frontmatter 加 footer: false

页脚显隐判定(isDocPage)
「文档型页面不挂页脚」——这类页面有左侧目录树、右侧本页目录、
底部上一页/下一页翻页,读者正在阅读流里,450px 的品牌收尾区是纯噪音。
命中任一条件即隐藏:
  1) 路径 /projects/<id>/manual/...
  2) frontmatter 有 sidebar / outline / aside
  3) frontmatter 显式 footer: false

frontmatter 的 footer 可双向覆盖(这解决了 /projects/ 的困境):
  该页有 outline: deep,按上面的规则本会被判成文档页而隐藏页脚,
  但它本质是「浏览页」,需要显式 footer: false 才符合预期。
  同理 footer: true 可强制显示。

验证(干净 clone 构建后实测)
- /projects/        无页脚(footer=false,无版权条)
- /                  有页脚(453px,渐变背景正常)
- 文档页(详情/对比/快速开始/手册)均无页脚
- 自定义整页(关于/联系/演示/官方文档)均有页脚
2026-11-06 13:39:41 +08:00

91 lines
3.5 KiB
TypeScript

import DefaultTheme from 'vitepress/theme'
import type { Theme } from 'vitepress'
import { h, watch, nextTick, computed } from 'vue'
import { useData, useRoute } from 'vitepress'
import ProjectList from './components/ProjectList.vue'
import HomePage from './components/HomePage.vue'
import CapabilityTabs from './components/CapabilityTabs.vue'
import FeatureGrid from './components/FeatureGrid.vue'
import ScreenshotPreview from './components/ScreenshotPreview.vue'
import PortalFooter from './components/PortalFooter.vue'
import { setupNavAnchorHighlight, applyNavAnchorHighlight } from './nav-anchor'
import './styles/portal.css'
/**
* 首页用 layout: page 承载自定义整页排版,
* 这里给它套一层 .portal-page,把默认窄栏与 padding 去掉。
* 同时接管导航栏锚点项的高亮(VitePress 默认只认 path,不认 hash)。
*/
const PortalLayout = () => {
const { frontmatter } = useData()
const route = useRoute()
const isPortal = frontmatter.value.layout === 'page'
/**
* 是否隐藏整站页脚。
*
* 判定原则:**文档型页面不挂页脚**。
* 这类页面有左侧目录树、右侧本页目录、底部上一页/下一页翻页,
* 读者正处在阅读流里,页脚那 450px 的品牌/联系/版权是纯噪音,
* 还会和翻页按钮挤在一起。
*
* 覆盖三种情况:
* 1) 路径命中项目手册 `/projects/<id>/manual/...`
* 2) frontmatter 写了 `sidebar` / `outline` / `aside`(文档页标记)
* 3) frontmatter 显式 `footer: false`
*
* frontmatter 的 `footer` 可双向覆盖:
* footer: false → 强制隐藏(如清单页 /projects/:有 outline 但本质是浏览页)
* footer: true → 强制显示
*
* 自定义整页(layout: page,如首页/关于/演示/官方文档)不在此列,
* 它们有完整的品牌收尾节奏,页脚保留。
*/
const isManualPath = computed(() => /^\/projects\/[^/]+\/manual(\/|$)/.test(route.path))
const isDocPage = computed(() => {
const fm = frontmatter.value as Record<string, unknown>
if (fm.layout === 'page' || fm.layout === 'home') return false
if (fm.footer === false) return true
if (fm.footer === true) return false
const hasDocMarks =
fm.sidebar !== undefined || fm.outline !== undefined || fm.aside !== undefined
return isManualPath.value || hasDocMarks
})
/**
* Layout 是函数式组件,onMounted 在这里不生效(Vue 会告警),
* 因此直接在渲染期间调度:等 DOM 落地后跑一次,再靠 MutationObserver 兜住后续变化。
*/
if (typeof window !== 'undefined') {
nextTick(() => setupNavAnchorHighlight())
}
// SPA 路由切换不触发 hashchange,这里补一次
watch(
() => [route.path, route.hash],
() => nextTick(() => applyNavAnchorHighlight())
)
return h('div', { class: isPortal ? 'portal-page' : '' }, h(DefaultTheme.Layout, null, {
// 自定义整站页脚(品牌 + 多栏链接 + 联系方式 + 版权条)
// 文档型页面一律不挂(见 isDocPage 判定)
'layout-bottom': isDocPage.value ? undefined : () => h(PortalFooter),
}))
}
export default {
extends: DefaultTheme,
Layout: PortalLayout,
enhanceApp({ app }) {
app.component('ProjectList', ProjectList)
app.component('HomePage', HomePage)
app.component('CapabilityTabs', CapabilityTabs)
app.component('FeatureGrid', FeatureGrid)
app.component('ScreenshotPreview', ScreenshotPreview)
},
} satisfies Theme