mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-26 11:38:15 +08:00
feat(theme): add support blog as home page
This commit is contained in:
parent
ccbfc16db5
commit
f60e4ea63e
@ -104,11 +104,11 @@
|
||||
"local-pkg": "^0.5.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"vue": "^3.4.38",
|
||||
"vue-router": "^4.4.3",
|
||||
"vuepress-plugin-md-enhance": "2.0.0-rc.52",
|
||||
"vuepress-plugin-md-power": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.243"
|
||||
"@iconify/json": "^2.2.243",
|
||||
"vue-router": "^4.4.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,11 +9,17 @@ import VPBlogNav from '@theme/Blog/VPBlogNav.vue'
|
||||
import VPTransitionFadeSlideY from '@theme/VPTransitionFadeSlideY.vue'
|
||||
import { useData } from '../../composables/index.js'
|
||||
|
||||
defineProps<{
|
||||
homeBlog?: boolean
|
||||
type?: string
|
||||
onlyOnce?: boolean
|
||||
}>()
|
||||
|
||||
const { theme, page } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vp-blog">
|
||||
<div class="vp-blog" :class="{ 'home-blog': homeBlog }">
|
||||
<slot name="blog-top" />
|
||||
|
||||
<div class="blog-container" :class="{ 'no-profile': !theme.profile }">
|
||||
@ -53,7 +59,7 @@ const { theme, page } = useData()
|
||||
<slot name="blog-categories-content-before" />
|
||||
</template>
|
||||
</VPBlogCategories>
|
||||
<VPPostList v-else>
|
||||
<VPPostList v-else :home-blog="homeBlog">
|
||||
<template #blog-post-list-before>
|
||||
<slot name="blog-post-list-before" />
|
||||
</template>
|
||||
@ -96,6 +102,12 @@ const { theme, page } = useData()
|
||||
transition: background-color var(--t-color);
|
||||
}
|
||||
|
||||
@media(min-width: 419px) {
|
||||
.vp-blog.home-blog {
|
||||
background-color: var(--vp-c-bg-alt);
|
||||
}
|
||||
}
|
||||
|
||||
.blog-container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import VPTransitionDrop from '@theme/VPTransitionDrop.vue'
|
||||
import VPPostItem from '@theme/Blog/VPPostItem.vue'
|
||||
import VPPagination from '@theme/Blog/VPPagination.vue'
|
||||
import { usePostListControl } from '../../composables/index.js'
|
||||
|
||||
const props = defineProps<{
|
||||
homeBlog?: boolean
|
||||
}>()
|
||||
|
||||
const {
|
||||
postList,
|
||||
page,
|
||||
@ -13,7 +18,7 @@ const {
|
||||
isFirstPage,
|
||||
isPaginationEnabled,
|
||||
changePage,
|
||||
} = usePostListControl()
|
||||
} = usePostListControl(computed(() => !!props.homeBlog))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@ -1,13 +1,33 @@
|
||||
<script lang="ts" setup>
|
||||
import { type Component, computed, nextTick, onUnmounted, resolveComponent, watch } from 'vue'
|
||||
import type { Component } from 'vue'
|
||||
import { computed, h, nextTick, onUnmounted, resolveComponent, watch } from 'vue'
|
||||
import VPHomeBanner from '@theme/Home/VPHomeBanner.vue'
|
||||
import VPHomeHero from '@theme/Home/VPHomeHero.vue'
|
||||
import VPHomeFeatures from '@theme/Home/VPHomeFeatures.vue'
|
||||
import VPHomeTextImage from '@theme/Home/VPHomeTextImage.vue'
|
||||
import VPHomeProfile from '@theme/Home/VPHomeProfile.vue'
|
||||
import VPHomeCustom from '@theme/Home/VPHomeCustom.vue'
|
||||
import VPBlog from '@theme/Blog/VPBlog.vue'
|
||||
import { useData } from '../../composables/index.js'
|
||||
|
||||
const slots = defineSlots<{
|
||||
'blog-top': () => any
|
||||
'blog-bottom': () => any
|
||||
'blog-post-list-before': () => any
|
||||
'blog-post-list-after': () => any
|
||||
'blog-post-list-pagination-after': () => any
|
||||
}>()
|
||||
|
||||
function VPHomeBlog() {
|
||||
return h(VPBlog, { homeBlog: true }, {
|
||||
'blog-top': () => slots['blog-top']?.(),
|
||||
'blog-bottom': () => slots['blog-bottom']?.(),
|
||||
'blog-post-list-before': () => slots['blog-post-list-before']?.(),
|
||||
'blog-post-list-after': () => slots['blog-post-list-after']?.(),
|
||||
'blog-post-list-pagination-after': () => slots['blog-post-list-pagination-after']?.(),
|
||||
})
|
||||
}
|
||||
|
||||
const components: Record<string, Component<any, any, any>> = {
|
||||
'banner': VPHomeBanner,
|
||||
'hero': VPHomeHero,
|
||||
@ -15,6 +35,7 @@ const components: Record<string, Component<any, any, any>> = {
|
||||
'text-image': VPHomeTextImage,
|
||||
'image-text': VPHomeTextImage,
|
||||
'profile': VPHomeProfile,
|
||||
'blog': VPHomeBlog,
|
||||
'custom': VPHomeCustom,
|
||||
}
|
||||
|
||||
|
||||
@ -99,7 +99,23 @@ watch([isBlogLayout, () => frontmatter.value.pageLayout], () => nextTick(() =>
|
||||
|
||||
<VPFriends v-else-if="frontmatter.pageLayout === 'friends'" />
|
||||
|
||||
<VPHome v-else-if="frontmatter.pageLayout === 'home'" />
|
||||
<VPHome v-else-if="frontmatter.pageLayout === 'home'">
|
||||
<template #blog-top>
|
||||
<slot name="blog-top" />
|
||||
</template>
|
||||
<template #blog-bottom>
|
||||
<slot name="blog-bottom" />
|
||||
</template>
|
||||
<template #blog-post-list-before>
|
||||
<slot name="blog-post-list-before" />
|
||||
</template>
|
||||
<template #blog-post-list-after>
|
||||
<slot name="blog-post-list-after" />
|
||||
</template>
|
||||
<template #blog-post-list-pagination-after>
|
||||
<slot name="blog-post-list-pagination-after" />
|
||||
</template>
|
||||
</VPHome>
|
||||
|
||||
<component :is="frontmatter.pageLayout" v-else-if="frontmatter.pageLayout && frontmatter.pageLayout !== 'doc'" />
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { computed } from 'vue'
|
||||
import { type Ref, computed } from 'vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import type { PlumeThemeBlogPostItem } from '../../shared/index.js'
|
||||
import { useLocalePostList } from './blog-data.js'
|
||||
@ -7,7 +7,7 @@ import { useRouteQuery } from './route-query.js'
|
||||
|
||||
const DEFAULT_PER_PAGE = 10
|
||||
|
||||
export function usePostListControl() {
|
||||
export function usePostListControl(homePage: Ref<boolean>) {
|
||||
const { theme } = useData()
|
||||
|
||||
const list = useLocalePostList()
|
||||
@ -111,7 +111,15 @@ export function usePostListControl() {
|
||||
if (page.value === current)
|
||||
return
|
||||
page.value = current
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'instant' })
|
||||
setTimeout(() => {
|
||||
let top = 0
|
||||
if (homePage.value) {
|
||||
top = document.querySelector('.vp-blog')?.getBoundingClientRect().top || 0
|
||||
top += window.scrollY - 64
|
||||
}
|
||||
|
||||
window.scrollTo({ top, behavior: 'instant' })
|
||||
}, 0)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -67,3 +67,15 @@ export function scrollTo(
|
||||
window.scrollTo({ top, behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
|
||||
export function getOffsetTop<T extends HTMLElement = HTMLElement>(target: T | null): number {
|
||||
if (!target)
|
||||
return 0
|
||||
let parent: HTMLElement | null = target
|
||||
let top = 0
|
||||
while (parent) {
|
||||
top += parent.offsetTop
|
||||
parent = parent.offsetParent as HTMLElement
|
||||
}
|
||||
return top
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export * from './shared.js'
|
||||
export * from './dom.js'
|
||||
// export * from './dom.js'
|
||||
export * from './resolveEditLink.js'
|
||||
export * from './resolveRepoType.js'
|
||||
export * from './resolveNavLink.js'
|
||||
export * from './animate.js'
|
||||
// export * from './animate.js'
|
||||
|
||||
@ -8,11 +8,12 @@ const FALLBACK_OPTIONS: PlumeThemeLocaleData = {
|
||||
appearance: true,
|
||||
|
||||
blog: {
|
||||
link: '/blog/',
|
||||
pagination: { perPage: 15 },
|
||||
postList: true,
|
||||
tags: true,
|
||||
archives: true,
|
||||
categories: true,
|
||||
link: '/blog/',
|
||||
tagsLink: '/blog/tags/',
|
||||
archivesLink: '/blog/archives/',
|
||||
categoriesLink: '/blog/categories/',
|
||||
|
||||
@ -39,10 +39,12 @@ export async function setupPage(
|
||||
const locale = localePath === '/' ? rootPath : localePath
|
||||
|
||||
// 添加 博客页面
|
||||
pageList.push(createPage(app, {
|
||||
path: withBase(link, localePath),
|
||||
frontmatter: { lang, _pageLayout: 'blog', title: getTitle(locale, 'blog') },
|
||||
}))
|
||||
if (blog.postList !== false) {
|
||||
pageList.push(createPage(app, {
|
||||
path: withBase(link, localePath),
|
||||
frontmatter: { lang, _pageLayout: 'blog', title: getTitle(locale, 'blog') },
|
||||
}))
|
||||
}
|
||||
|
||||
// 添加 标签页
|
||||
if (blog.tags !== false) {
|
||||
@ -108,6 +110,11 @@ export function extendsPageData(
|
||||
delete page.frontmatter._pageLayout
|
||||
}
|
||||
|
||||
if (page.frontmatter.pageLayout === 'blog') {
|
||||
page.frontmatter.draft = true
|
||||
page.data.type = 'blog'
|
||||
}
|
||||
|
||||
if ('externalLink' in page.frontmatter) {
|
||||
page.frontmatter.externalLinkIcon = page.frontmatter.externalLink
|
||||
delete page.frontmatter.externalLink
|
||||
|
||||
@ -16,13 +16,6 @@ export type PlumeThemeBlogPostData = PlumeThemeBlogPostItem[]
|
||||
|
||||
export interface PlumeThemeBlog {
|
||||
|
||||
/**
|
||||
* 博客文章列表页链接
|
||||
*
|
||||
* @default '/blog/'
|
||||
*/
|
||||
link?: string
|
||||
|
||||
/**
|
||||
* 通过 glob string 配置包含文件,
|
||||
*
|
||||
@ -55,6 +48,19 @@ export interface PlumeThemeBlog {
|
||||
perPage?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 博客文章列表页链接
|
||||
*
|
||||
* @default '/blog/'
|
||||
*/
|
||||
link?: string
|
||||
|
||||
/**
|
||||
* 是否启用博客文章列表
|
||||
* @default true
|
||||
*/
|
||||
postList?: boolean
|
||||
|
||||
/**
|
||||
* 是否启用标签页
|
||||
* @default true
|
||||
|
||||
@ -19,7 +19,7 @@ export interface PlumeNormalFrontmatter extends PageFrontmatter {
|
||||
/**
|
||||
* page layout
|
||||
*/
|
||||
pageLayout?: false | 'home' | 'doc' | 'custom' | 'page' | 'friends'
|
||||
pageLayout?: false | 'home' | 'blog' | 'doc' | 'custom' | 'page' | 'friends'
|
||||
|
||||
/**
|
||||
* 自定义页面 class
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user