mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
feat(theme): split the post cover field into cover and coverStyle (#512)
This commit is contained in:
parent
d4e76e0b0b
commit
4227b8a91e
@ -56,21 +56,22 @@ tags:
|
||||
|
||||
### cover
|
||||
|
||||
- 类型: `string \| BlogPostCover`
|
||||
- 类型: `string`
|
||||
- 默认值: `''`
|
||||
|
||||
文章封面图。 封面图仅显示在 文章列表页。
|
||||
|
||||
当传入为 `string` 时,表示 封面图链接地址。仅支持 绝对路径 以及 远程图片地址。
|
||||
仅支持 绝对路径 以及 远程图片地址。
|
||||
|
||||
当传入为 `BlogPostCover` 时,表示 封面图配置。
|
||||
### coverStyle
|
||||
|
||||
- 类型: `BlogPostCoverStyle`
|
||||
- 默认值: `null
|
||||
|
||||
封面图配置。
|
||||
|
||||
```ts
|
||||
interface BlogPostCover {
|
||||
/**
|
||||
* 封面图链接地址,只能使用 绝对路径 以及 远程图片地址
|
||||
*/
|
||||
url: string
|
||||
interface BlogPostCoverStyle {
|
||||
/**
|
||||
* 博客文章封面图的位置
|
||||
*/
|
||||
|
||||
@ -162,16 +162,12 @@ interface BlogOptions {
|
||||
*
|
||||
* @default 'right'
|
||||
*/
|
||||
postCover?: BlogPostCoverLayout | Omit<BlogPostCover, 'url'>
|
||||
postCover?: BlogPostCoverLayout | BlogPostCoverStyle
|
||||
}
|
||||
|
||||
type BlogPostCoverLayout = 'left' | 'right' | 'odd-left' | 'odd-right' | 'top'
|
||||
|
||||
interface BlogPostCover {
|
||||
/**
|
||||
* 封面图链接地址,只能使用 绝对路径 以及 远程图片地址
|
||||
*/
|
||||
url: string
|
||||
interface BlogPostCoverStyle {
|
||||
/**
|
||||
* 博客文章封面图的位置
|
||||
*/
|
||||
|
||||
@ -128,7 +128,8 @@ tags:
|
||||
| tags | `string[]` | `[]` | 文章标签 |
|
||||
| sticky | `boolean \| number` | false | 是否置顶, 如果为数字,则数字越大,置顶越靠前 |
|
||||
| draft | `boolean` | false | 是否为草稿,草稿文章不会被展示 |
|
||||
| cover | `string \| BlogPostCover` | `''` | 文章封面 |
|
||||
| cover | `string` | `''` | 文章封面 |
|
||||
| coverStyle | `BlogPostCoverStyle` | `null` | 文章封面样式 |
|
||||
| excerpt | `boolean \| string` | '' | 文章摘要,默认通过 `<!-- more -->` 注释生成, 传入字符串表示自定义内容,不再从正文提取 |
|
||||
|
||||
除了以上的字段,你还可以使用 [通用 frontmatter 配置](../config/frontmatter/basic.md) 中的字段,
|
||||
@ -214,8 +215,8 @@ cover: /images/cover.jpg # [!code ++]
|
||||
```md
|
||||
---
|
||||
title: 文章标题
|
||||
cover: # [!code ++:5]
|
||||
url: /images/cover.jpg
|
||||
cover: /images/cover.jpg # [!code ++:5]
|
||||
coverStyle:
|
||||
layout: left
|
||||
ratio: 16:9
|
||||
width: 300
|
||||
@ -228,7 +229,7 @@ cover: # [!code ++:5]
|
||||
<VPPostItem
|
||||
:post="{ path: '/article/ecxnxxd0/', title: '文章标题',
|
||||
categoryList: [{id:'65f30c',sort:4,name:'教程'}], createTime: '2024/09/18 09:19:36',
|
||||
lang:'zh-CN', excerpt:'', cover: { url: 'https://api.pengzhanbo.cn/wallpaper/bing', layout: 'left', ratio: '16:9', width: 300 } }"
|
||||
lang:'zh-CN', excerpt:'', cover: 'https://api.pengzhanbo.cn/wallpaper/bing', coverStyle: { layout: 'left', ratio: '16:9', width: 300 } }"
|
||||
:index="1"
|
||||
/>
|
||||
</div>
|
||||
@ -238,8 +239,8 @@ cover: # [!code ++:5]
|
||||
```md
|
||||
---
|
||||
title: 文章标题
|
||||
cover: # [!code ++:6]
|
||||
url: /images/cover.jpg
|
||||
cover: /images/cover.jpg # [!code ++:6]
|
||||
coverStyle:
|
||||
layout: left
|
||||
ratio: 16:9
|
||||
width: 300
|
||||
@ -253,8 +254,8 @@ cover: # [!code ++:6]
|
||||
<VPPostItem
|
||||
:post="{ path: '/article/ecxnxxd0/', title: '文章标题',
|
||||
categoryList: [{id:'65f30c',sort:4,name:'教程'}], createTime: '2024/09/18 09:19:36',
|
||||
lang:'zh-CN', excerpt:'',
|
||||
cover: { url: 'https://api.pengzhanbo.cn/wallpaper/bing', layout: 'left', ratio: '16:9', width: 300, compact: true } }"
|
||||
lang:'zh-CN', excerpt:'', cover: 'https://api.pengzhanbo.cn/wallpaper/bing',
|
||||
coverStyle: { layout: 'left', ratio: '16:9', width: 300, compact: true } }"
|
||||
:index="1"
|
||||
/>
|
||||
</div>
|
||||
@ -267,8 +268,8 @@ cover: # [!code ++:6]
|
||||
```md
|
||||
---
|
||||
title: 文章标题
|
||||
cover: # [!code ++:5]
|
||||
url: /images/cover.jpg
|
||||
cover: /images/cover.jpg # [!code ++:5]
|
||||
coverStyle:
|
||||
layout: top
|
||||
ratio: 16:9
|
||||
width: 300
|
||||
@ -281,8 +282,8 @@ cover: # [!code ++:5]
|
||||
<VPPostItem
|
||||
:post="{ path: '/article/ecxnxxd0/', title: '文章标题',
|
||||
categoryList: [{id:'65f30c',sort:4,name:'教程'}], createTime: '2024/09/18 09:19:36',
|
||||
lang:'zh-CN', excerpt:'',
|
||||
cover: { url: 'https://api.pengzhanbo.cn/wallpaper/bing', layout: 'top', ratio: '16:9', width: 300 } }"
|
||||
lang:'zh-CN', excerpt:'', cover: 'https://api.pengzhanbo.cn/wallpaper/bing',
|
||||
coverStyle: { layout: 'top', ratio: '16:9', width: 300 } }"
|
||||
:index="1"
|
||||
/>
|
||||
</div>
|
||||
@ -318,11 +319,7 @@ export default defineUserConfig({
|
||||
```ts
|
||||
type BlogPostCoverLayout = 'left' | 'right' | 'odd-left' | 'odd-right' | 'top'
|
||||
|
||||
interface BlogPostCover {
|
||||
/**
|
||||
* 封面图链接地址,只能使用 绝对路径 以及 远程图片地址
|
||||
*/
|
||||
url: string
|
||||
interface BlogPostCoverStyle {
|
||||
/**
|
||||
* 博客文章封面图的位置
|
||||
*/
|
||||
@ -359,22 +356,22 @@ interface BlogPostCover {
|
||||
<VPPostItem
|
||||
:post="{ path: '/article/ecxnxxd0/', title: '文章标题',
|
||||
categoryList: [{id:'65f30c',sort:4,name:'教程'}], createTime: '2024/09/18 09:19:36',
|
||||
lang:'zh-CN', excerpt:'',
|
||||
cover: { url: 'https://api.pengzhanbo.cn/wallpaper/bing', layout: 'odd-left', ratio: '16:9', width: 300, compact: true } }"
|
||||
lang:'zh-CN', excerpt:'', cover: 'https://api.pengzhanbo.cn/wallpaper/bing',
|
||||
coverStyle: { layout: 'odd-left', ratio: '16:9', width: 300, compact: true } }"
|
||||
:index="0"
|
||||
/>
|
||||
<VPPostItem
|
||||
:post="{ path: '/article/ecxnxxd0/', title: '文章标题',
|
||||
categoryList: [{id:'65f30c',sort:4,name:'教程'}], createTime: '2024/09/18 09:19:36',
|
||||
lang:'zh-CN', excerpt:'',
|
||||
cover: { url: 'https://api.pengzhanbo.cn/wallpaper/bing', layout: 'odd-left', ratio: '16:9', width: 300,compact: true } }"
|
||||
lang:'zh-CN', excerpt:'', cover: 'https://api.pengzhanbo.cn/wallpaper/bing',
|
||||
coverStyle: { layout: 'odd-left', ratio: '16:9', width: 300,compact: true } }"
|
||||
:index="1"
|
||||
/>
|
||||
<VPPostItem
|
||||
:post="{ path: '/article/ecxnxxd0/', title: '文章标题',
|
||||
categoryList: [{id:'65f30c',sort:4,name:'教程'}], createTime: '2024/09/18 09:19:36',
|
||||
lang:'zh-CN', excerpt:'',
|
||||
cover: { url: 'https://api.pengzhanbo.cn/wallpaper/bing', layout: 'odd-left', ratio: '16:9', width: 300, compact: true } }"
|
||||
lang:'zh-CN', excerpt:'', cover: 'https://api.pengzhanbo.cn/wallpaper/bing',
|
||||
coverStyle: { layout: 'odd-left', ratio: '16:9', width: 300, compact: true } }"
|
||||
:index="2"
|
||||
/>
|
||||
</div>
|
||||
@ -477,7 +474,7 @@ config:
|
||||
这导致存在了重复功能的页面,为此,你需要 [主题配置 > 博客配置](../config/主题配置.md#blog) 中,
|
||||
**关闭自动生成博客文章列表页**:
|
||||
|
||||
(还可以在重新修改 分类页/标签页/归档页的链接地址)
|
||||
(还可以重新修改 分类页/标签页/归档页的链接地址)
|
||||
|
||||
::: code-tabs
|
||||
@tab .vuepress/config.ts
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import type { BlogPostCover, PlumeThemeBlogPostItem } from '../../../shared/index.js'
|
||||
import type { BlogPostCoverStyle, PlumeThemeBlogPostItem } from '../../../shared/index.js'
|
||||
import VPLink from '@theme/VPLink.vue'
|
||||
import { useMediaQuery } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
@ -11,6 +11,7 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const { blog } = useData()
|
||||
const isMobile = useMediaQuery('(max-width: 496px)')
|
||||
const colors = useTagColors()
|
||||
const { categories: categoriesLink, tags: tagsLink } = useInternalLink()
|
||||
|
||||
@ -38,15 +39,14 @@ const tags = computed(() => {
|
||||
}))
|
||||
})
|
||||
|
||||
const cover = computed<BlogPostCover | null>(() => {
|
||||
const cover = computed<BlogPostCoverStyle | null>(() => {
|
||||
if (!props.post.cover)
|
||||
return null
|
||||
const opt = blog.value.postCover ?? 'right'
|
||||
const options = typeof opt === 'string' ? { layout: opt } : opt
|
||||
const cover = typeof props.post.cover === 'string' ? { url: props.post.cover } : props.post.cover
|
||||
return { layout: 'right', ratio: '4:3', ...options, ...cover }
|
||||
return { layout: 'right', ratio: '4:3', ...options, ...props.post.coverStyle }
|
||||
})
|
||||
const isMobile = useMediaQuery('(max-width: 496px)')
|
||||
|
||||
const coverLayout = computed(() => {
|
||||
if (isMobile.value)
|
||||
return 'top'
|
||||
@ -58,11 +58,13 @@ const coverLayout = computed(() => {
|
||||
return odd ? 'right' : 'left'
|
||||
return layout
|
||||
})
|
||||
|
||||
const coverCompact = computed(() => {
|
||||
if (props.post.excerpt || coverLayout.value === 'top')
|
||||
return false
|
||||
return cover.value?.compact ?? false
|
||||
})
|
||||
|
||||
const coverStyles = computed(() => {
|
||||
if (!cover.value)
|
||||
return null
|
||||
@ -84,9 +86,15 @@ const coverStyles = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="vp-blog-post-item" data-allow-mismatch :class="{ 'has-cover': cover, [coverLayout]: cover }">
|
||||
<div v-if="cover" class="post-cover" data-allow-mismatch :class="{ compact: coverCompact }" :style="coverStyles">
|
||||
<img :src="cover.url" :alt="post.title" loading="lazy">
|
||||
<div
|
||||
class="vp-blog-post-item" data-allow-mismatch
|
||||
:class="{ 'has-cover': props.post.cover, [coverLayout]: cover }"
|
||||
>
|
||||
<div
|
||||
v-if="props.post.cover" class="post-cover" data-allow-mismatch
|
||||
:class="{ compact: coverCompact }" :style="coverStyles"
|
||||
>
|
||||
<img :src="props.post.cover" :alt="post.title" loading="lazy">
|
||||
</div>
|
||||
<div class="blog-post-item-content">
|
||||
<h3>
|
||||
|
||||
@ -19,10 +19,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
size: 'medium',
|
||||
theme: 'brand',
|
||||
text: '',
|
||||
tag: undefined,
|
||||
href: undefined,
|
||||
target: undefined,
|
||||
rel: undefined,
|
||||
})
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
@ -79,6 +79,7 @@ const groups = computed(() => matter.value.groups || [])
|
||||
}
|
||||
|
||||
.edit-link {
|
||||
display: flex;
|
||||
padding-left: 1rem;
|
||||
margin-top: 64px;
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import { removeLeadingSlash } from '@vuepress/helper'
|
||||
import { createFilter } from 'create-filter'
|
||||
import dayjs from 'dayjs'
|
||||
import { resolveNotesOptions } from '../config/index.js'
|
||||
import { normalizePath, perfLog, perfMark, resolveContent, writeTemp } from '../utils/index.js'
|
||||
import { logger, normalizePath, perfLog, perfMark, resolveContent, writeTemp } from '../utils/index.js'
|
||||
import { isEncryptPage } from './prepareEncrypt.js'
|
||||
|
||||
const HEADING_RE = /<h(\d)[^>]*>.*?<\/h\1>/gi
|
||||
@ -75,6 +75,12 @@ export async function preparedBlogData(
|
||||
lang: page.lang,
|
||||
excerpt: '',
|
||||
cover: page.data.frontmatter.cover,
|
||||
coverStyle: page.data.frontmatter.coverStyle,
|
||||
}
|
||||
|
||||
// FIXME validate post cover
|
||||
if (typeof data.cover === 'object') {
|
||||
logger.warn(`cover should be a path string, please use string instead. (${page.filePathRelative})`)
|
||||
}
|
||||
|
||||
if (isEncryptPage(page, encrypt)) {
|
||||
|
||||
@ -65,11 +65,7 @@ export type CopyrightLicense = LiteralUnion<KnownCopyrightLicense>
|
||||
|
||||
export type BlogPostCoverLayout = 'left' | 'right' | 'odd-left' | 'odd-right' | 'top'
|
||||
|
||||
export interface BlogPostCover {
|
||||
/**
|
||||
* 封面图链接地址,只能使用 绝对路径 以及 远程图片地址
|
||||
*/
|
||||
url: string
|
||||
export interface BlogPostCoverStyle {
|
||||
/**
|
||||
* 博客文章封面图的位置
|
||||
*/
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { BlogPostCover, CopyrightLicense } from '../base.js'
|
||||
import type { BlogPostCoverStyle, CopyrightLicense } from '../base.js'
|
||||
import type { CopyrightOptions } from '../options/copyright.js'
|
||||
import type { PlumeThemePageFrontmatter } from './page.js'
|
||||
|
||||
@ -33,7 +33,9 @@ export interface PlumeThemePostFrontmatter extends PlumeThemePageFrontmatter {
|
||||
/**
|
||||
* 文章封面图
|
||||
*/
|
||||
cover?: string | BlogPostCover
|
||||
cover?: string
|
||||
|
||||
coverStyle?: BlogPostCoverStyle
|
||||
|
||||
/**
|
||||
* 是否展示文章摘要,传入 string 时为自定义摘要,此时 `<!-- more -->` 无效
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { BlogPostCover, BlogPostCoverLayout } from '../base.js'
|
||||
import type { BlogPostCoverLayout, BlogPostCoverStyle } from '../base.js'
|
||||
import type { PageCategoryData } from '../page-data.js'
|
||||
|
||||
export interface BlogOptions {
|
||||
@ -114,5 +114,5 @@ export interface BlogOptions {
|
||||
*
|
||||
* @default 'right'
|
||||
*/
|
||||
postCover?: BlogPostCoverLayout | Omit<BlogPostCover, 'url'>
|
||||
postCover?: BlogPostCoverLayout | BlogPostCoverStyle
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { GitPluginPageData } from '@vuepress/plugin-git'
|
||||
import type { BlogPostCover } from './base.js'
|
||||
import type { BlogPostCoverStyle } from './base.js'
|
||||
|
||||
interface ReadingTime {
|
||||
/** 分钟数 */
|
||||
@ -32,7 +32,8 @@ export interface PlumeThemeBlogPostItem {
|
||||
createTime: string
|
||||
lang: string
|
||||
encrypt?: boolean
|
||||
cover?: string | BlogPostCover
|
||||
cover?: string
|
||||
coverStyle?: BlogPostCoverStyle
|
||||
}
|
||||
|
||||
export type PlumeThemeBlogPostData = PlumeThemeBlogPostItem[]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user