diff --git a/docs/notes/theme/config/主题配置.md b/docs/notes/theme/config/主题配置.md index 0b6e03db..cd1753ef 100644 --- a/docs/notes/theme/config/主题配置.md +++ b/docs/notes/theme/config/主题配置.md @@ -104,6 +104,17 @@ interface BlogOptions { */ tagsLink?: string + /** + * 标签颜色主题 + * + * - `colored`: 彩色标签,不同标签颜色不同 + * - `brand`: 使用主题颜色作为标签颜色 + * - `gray`: 使用 灰色 作为标签颜色 + * + * @default 'colored' + */ + tagsTheme?: 'colored' | 'gray' | 'brand' + /** * 是否启用归档页 * @default true diff --git a/theme/src/client/components/Blog/VPBlogTags.vue b/theme/src/client/components/Blog/VPBlogTags.vue index 2fb1febf..1292c337 100644 --- a/theme/src/client/components/Blog/VPBlogTags.vue +++ b/theme/src/client/components/Blog/VPBlogTags.vue @@ -94,11 +94,11 @@ const { tags, currentTag, postList, handleTagClick } = useTags() padding: 6px 10px 6px 12px; font-size: 14px; line-height: 1; - color: var(--vp-c-bg); + color: var(--vp-tag-color); word-wrap: break-word; cursor: pointer; - background-color: var(--vp-tag-color); - border: solid 1px var(--vp-tag-color); + background-color: var(--vp-tag-bg); + border: 1px solid var(--vp-tag-bg); border-radius: 6px; transition: all var(--vp-t-color); } @@ -117,8 +117,8 @@ const { tags, currentTag, postList, handleTagClick } = useTags() display: inline-block; padding-left: 6px; margin-left: 4px; - color: var(--vp-c-bg); - border-left: 1px solid var(--vp-c-bg); + color: var(--vp-tag-color); + border-left: 1px solid var(--vp-tag-color); transition: color var(--vp-t-color), border-left var(--vp-t-color); } diff --git a/theme/src/client/components/Blog/VPPostItem.vue b/theme/src/client/components/Blog/VPPostItem.vue index 87c84672..cce9ebd7 100644 --- a/theme/src/client/components/Blog/VPPostItem.vue +++ b/theme/src/client/components/Blog/VPPostItem.vue @@ -27,14 +27,18 @@ const sticky = computed(() => { return false }) -const tags = computed(() => - (props.post.tags ?? []) +const tags = computed(() => { + const blog = theme.value.blog || {} + const tagTheme = blog.tagsTheme ?? 'colored' + + return (props.post.tags ?? []) .slice(0, 4) .map(tag => ({ name: tag, - className: `vp-tag-${colors.value[tag]}`, - })), -) + className: colors.value[tag] ? `vp-tag-${colors.value[tag]}` : `tag-${tagTheme}`, + })) +}) + const cover = computed(() => { if (!props.post.cover) return null diff --git a/theme/src/client/components/VPDocMeta.vue b/theme/src/client/components/VPDocMeta.vue index 7e698754..5dd5f8ee 100644 --- a/theme/src/client/components/VPDocMeta.vue +++ b/theme/src/client/components/VPDocMeta.vue @@ -22,10 +22,12 @@ const createTime = computed(() => { }) const tags = computed(() => { + const blog = theme.value.blog || {} + const tagTheme = blog.tagsTheme ?? 'colored' if (matter.value.tags) { return matter.value.tags.slice(0, 4).map(tag => ({ name: tag, - className: `vp-tag-${colors.value[tag]}`, + className: colors.value[tag] ? `vp-tag-${colors.value[tag]}` : `tag-${tagTheme}`, })) } @@ -122,8 +124,8 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr margin-right: 6px; font-size: 12px; line-height: 1; - color: var(--vp-tag-color, var(--vp-c-text-3)); - background-color: var(--vp-tag-bg, var(--vp-c-default-soft)); + color: var(--vp-tag-color); + background-color: var(--vp-tag-bg); border-radius: 3px; } diff --git a/theme/src/client/composables/blog-tags.ts b/theme/src/client/composables/blog-tags.ts index 66bf5e48..c1b36db9 100644 --- a/theme/src/client/composables/blog-tags.ts +++ b/theme/src/client/composables/blog-tags.ts @@ -2,17 +2,22 @@ import type { PlumeThemeBlogPostItem } from '../../shared/index.js' import { computed } from 'vue' import { toArray } from '../utils/index.js' import { useLocalePostList } from './blog-data.js' +import { useData } from './data.js' import { useRouteQuery } from './route-query.js' import { useTagColors } from './tag-colors.js' type ShortPostItem = Pick export function useTags() { + const { theme } = useData() const list = useLocalePostList() const colors = useTagColors() const tags = computed(() => { + const blog = theme.value.blog || {} + const tagTheme = blog.tagsTheme ?? 'colored' + const tagMap: Record = {} list.value.forEach((item) => { if (item.tags) { @@ -27,7 +32,7 @@ export function useTags() { return Object.keys(tagMap).map(tag => ({ name: tag, count: tagMap[tag] > 99 ? '99+' : tagMap[tag], - className: `vp-tag-${colors.value[tag]}`, + className: colors.value[tag] ? `vp-tag-${colors.value[tag]}` : `tag-${tagTheme}`, })) }) diff --git a/theme/src/client/styles/vars.css b/theme/src/client/styles/vars.css index 0b209e01..8fe51352 100644 --- a/theme/src/client/styles/vars.css +++ b/theme/src/client/styles/vars.css @@ -689,6 +689,22 @@ --vp-mark-color-soft: var(--vp-c-brand-soft); } +/** + * post tag + * -------------------------------------------------------------------------- */ +.tag:not([class*="vp-tag-"]), +.tag.tag-gray { + --vp-tag-color: var(--vp-c-text-3); + --vp-tag-bg: var(--vp-c-default-soft); + --vp-tag-hover-color: var(--vp-c-text-3); +} + +.tag.tag-brand { + --vp-tag-color: var(--vp-c-brand-1); + --vp-tag-bg: var(--vp-c-brand-soft); + --vp-tag-hover-color: var(--vp-c-brand-2); +} + /** * Compatible with `vuepress` guidelines * -------------------------------------------------------------------------- */ diff --git a/theme/src/node/prepare/index.ts b/theme/src/node/prepare/index.ts index 399f0216..0db2fc11 100644 --- a/theme/src/node/prepare/index.ts +++ b/theme/src/node/prepare/index.ts @@ -14,7 +14,7 @@ export async function prepareData( const start = performance.now() const { localeOptions, encrypt } = getThemeConfig() await Promise.all([ - prepareArticleTagColors(app), + prepareArticleTagColors(app, localeOptions), preparedBlogData(app, localeOptions, encrypt), prepareSidebar(app, localeOptions), prepareEncrypt(app, encrypt), diff --git a/theme/src/node/prepare/prepareArticleTagColor.ts b/theme/src/node/prepare/prepareArticleTagColor.ts index cb0b5712..89f5e909 100644 --- a/theme/src/node/prepare/prepareArticleTagColor.ts +++ b/theme/src/node/prepare/prepareArticleTagColor.ts @@ -1,5 +1,7 @@ import type { App } from 'vuepress' +import type { PlumeThemeLocaleOptions } from '../../shared/index.js' import { toArray } from '@pengzhanbo/utils' +import { isPlainObject } from 'vuepress/shared' import { logger, nanoid, resolveContent, writeTemp } from '../utils/index.js' export type TagsColorsItem = readonly [ @@ -32,11 +34,16 @@ export const PRESET: TagsColorsItem[] = [ // { index: className } const cache: Record = {} -export async function prepareArticleTagColors(app: App): Promise { +export async function prepareArticleTagColors(app: App, localeOptions: PlumeThemeLocaleOptions): Promise { const start = performance.now() - const { js, css } = genCode(app) - await writeTemp(app, 'internal/articleTagColors.css', css) + const blog = isPlainObject(localeOptions.blog) ? localeOptions.blog : {} + + const { js, css } = genCode(app, blog.tagsTheme ?? 'colored') + if (css) + await writeTemp(app, 'internal/articleTagColors.css', css) + await writeTemp(app, 'internal/articleTagColors.js', js) + if (app.env.isDebug) { logger.info( `Generate article tag colors: ${(performance.now() - start).toFixed(2)}ms`, @@ -44,10 +51,20 @@ export async function prepareArticleTagColors(app: App): Promise { } } -export function genCode(app: App): { js: string, css: string } { +export function genCode(app: App, theme: 'colored' | 'brand' | 'gray'): { js: string, css: string } { const articleTagColors: Record = {} const tagList = new Set() + if (theme !== 'colored') { + return { + js: resolveContent(app, { + name: 'articleTagColors', + content: articleTagColors, + }), + css: '', + } + } + app.pages.forEach((page) => { const { frontmatter: { tags } } = page if (tags) { diff --git a/theme/src/shared/blog.ts b/theme/src/shared/blog.ts index 3a05116a..6b29730b 100644 --- a/theme/src/shared/blog.ts +++ b/theme/src/shared/blog.ts @@ -74,6 +74,13 @@ export interface PlumeThemeBlog { * @default '/blog/tags/' */ tagsLink?: string + + /** + * 标签颜色主题 + * + * @default 'colored' + */ + tagsTheme?: 'colored' | 'gray' | 'brand' /** * 是否启用归档页 * @default true