746 字
4 分钟Add commentMore actions
Fuwari添加置顶功能
示例
---title: Fuwari添加置顶功能published: 2025-03-09description: "静态博客养成计划"image: ""tags: ["Fuwari", "教程"]category: Guidesdraft: falsecomments: truepinned: false---
添加置顶功能
添加 mdi 图标
- 在
astro.config.mjs
文件中修改:
icon({ include: { mdi: ["*"], "preprocess: vitePreprocess(),": ["*"], "fa6-brands": ["*"], "fa6-regular": ["*"], "fa6-solid": ["*"], },}),
- 添加mdi依赖:
- 项目根目录处终端执行:
pnpm add -D @iconify-json/mdi
- 修改依赖包文件(可选):
// 在package.json中添加 "@iconify-json/mdi": "^1.2.3",
WARNING修改完所有的json、yaml强烈建议格式化一遍代码,这两种文件格式及其讲究缩进、标点等格式!
添加 pinned 属性
在 src/components/PostCard.astro
组件中 :
---import path from "node:path";import type { CollectionEntry } from "astro:content";import { Icon } from "astro-icon/components";import I18nKey from "../i18n/i18nKey";import { i18n } from "../i18n/translation";import { getDir } from "../utils/url-utils";import PostMetadata from "./PostMeta.astro";import ImageWrapper from "./misc/ImageWrapper.astro";
interface Props { class?: string; entry: CollectionEntry<"posts">; title: string; url: string; published: Date; updated?: Date; tags: string[]; category: string; image: string; description: string; draft: boolean; style: string; pinned?: boolean;}const { entry, title, url, published, updated, tags, category, image, description, style, pinned,} = Astro.props;const className = Astro.props.class;
const hasCover = image !== undefined && image !== null && image !== "";
const coverWidth = "28%";
const { remarkPluginFrontmatter } = await entry.render();---<div class:list={["card-base flex flex-col-reverse md:flex-col w-full rounded-[var(--radius-large)] overflow-hidden relative", className]} style={style}> <div class:list={["pl-6 md:pl-9 pr-6 md:pr-2 pt-6 md:pt-7 pb-6 relative", {"w-full md:w-[calc(100%_-_52px_-_12px)]": !hasCover, "w-full md:w-[calc(100%_-_var(--coverWidth)_-_12px)]": hasCover}]}> {pinned && <div class="absolute top-2 right-2 text-[var(--primary)]"> <Icon name="mdi:pin" class="w-5 h-5" /> </div>} <a href={url} class="transition group w-full block font-bold mb-3 text-3xl text-90 hover:text-[var(--primary)] dark:hover:text-[var(--primary)] active:text-[var(--title-active)] dark:active:text-[var(--title-active)] before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)] before:absolute before:top-[35px] before:left-[18px] before:hidden md:before:block "> {title} <Icon class="inline text-[2rem] text-[var(--primary)] md:hidden translate-y-0.5 absolute" name="material-symbols:chevron-right-rounded" ></Icon> <Icon class="text-[var(--primary)] text-[2rem] transition hidden md:inline absolute translate-y-0.5 opacity-0 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0" name="material-symbols:chevron-right-rounded"></Icon> </a>
48 collapsed lines
<!-- metadata --> <PostMetadata published={published} updated={updated} tags={tags} category={category} hideTagsForMobile={true} hideUpdateDate={true} class="mb-4"></PostMetadata>
<!-- description --> <div class:list={["transition text-75 mb-3.5 pr-4", {"line-clamp-2 md:line-clamp-1": !description}]}> { description || remarkPluginFrontmatter.excerpt } </div>
<!-- word count and read time --> <div class="text-sm text-black/30 dark:text-white/30 flex gap-4 transition"> <div>{remarkPluginFrontmatter.words} {" " + i18n(I18nKey.wordsCount)}</div> <div>|</div> <div>{remarkPluginFrontmatter.minutes} {" " + i18n(I18nKey.minutesCount)}</div> </div>
</div>
{hasCover && <a href={url} aria-label={title} class:list={["group", "max-h-[20vh] md:max-h-none mx-4 mt-4 -mb-2 md:mb-0 md:mx-0 md:mt-0", "md:w-[var(--coverWidth)] relative md:absolute md:top-3 md:bottom-3 md:right-3 rounded-xl overflow-hidden active:scale-95" ]} > <div class="absolute pointer-events-none z-10 w-full h-full group-hover:bg-black/30 group-active:bg-black/50 transition"></div> <div class="absolute pointer-events-none z-20 w-full h-full flex items-center justify-center "> <Icon name="material-symbols:chevron-right-rounded" class="transition opacity-0 group-hover:opacity-100 scale-50 group-hover:scale-100 text-white text-5xl"> </Icon> </div> <ImageWrapper src={image} basePath={path.join("content/posts/", getDir(entry.id))} alt="Cover Image of the Post" class="w-full h-full"> </ImageWrapper> </a>}
{!hasCover && <a href={url} aria-label={title} class="!hidden md:!flex btn-regular w-[3.25rem] absolute right-3 top-3 bottom-3 rounded-xl bg-[var(--enter-btn-bg)] hover:bg-[var(--enter-btn-bg-hover)] active:bg-[var(--enter-btn-bg-active)] active:scale-95 "> <Icon name="material-symbols:chevron-right-rounded" class="transition text-[var(--primary)] text-4xl mx-auto"> </Icon> </a> }</div><div class="transition border-t-[1px] border-dashed mx-6 border-black/10 dark:border-white/[0.15] last:border-t-0 md:hidden"></div>
<style define:vars={{coverWidth}}></style>
在 src\content\config.ts
添加pinned默认行为:
const postsCollection = defineCollection({ schema: z.object({ title: z.string(), published: z.date(), updated: z.date().optional(), draft: z.boolean().optional().default(false), description: z.string().optional().default(""), image: z.string().optional().default(""), tags: z.array(z.string()).optional().default([]), category: z.string().optional().default(""), lang: z.string().optional().default(""), comments: z.boolean().optional().default(false), pinned: z.boolean().optional().default(false),
/* For internal use */ prevTitle: z.string().default(""), prevSlug: z.string().default(""), nextTitle: z.string().default(""), nextSlug: z.string().default(""), }),});
在 src\type\config.ts
添加pinned:
export type BlogPostData = { body: string; title: string; published: Date; description: string; tags: string[]; draft?: boolean; image?: string; category?: string; prevTitle?: string; prevSlug?: string; nextTitle?: string; nextSlug?: string; comments?: boolean; pinned?: boolean;};
在 src/components/PostPage.astro
添加页面数据:
<div class="transition flex flex-col rounded-[var(--radius-large)] bg-[var(--card-bg)] py-1 md:py-0 md:bg-transparent md:gap-4 mb-4"> {page.data.map((entry: CollectionEntry<"posts">) => ( <PostCard entry={entry} title={entry.data.title} tags={entry.data.tags} category={entry.data.category} published={entry.data.published} updated={entry.data.updated} url={getPostUrlBySlug(entry.slug)} image={entry.data.image} description={entry.data.description} draft={entry.data.draft} pinned={entry.data.pinned} class:list="onload-animation" style={`animation-delay: calc(var(--content-delay) + ${delay++ * interval}ms);`} ></PostCard> ))}</div>
添加 pinned 核心代码
在 src/utils/content-utils.ts
的 sorterd
方法内修改分类方式 :
const sorted = allBlogPosts.sort((a, b) => { // 首先按置顶状态排序 if (a.data.pinned && !b.data.pinned) return -1; if (!a.data.pinned && b.data.pinned) return 1; // 然后按发布时间排序 const dateA = new Date(a.data.published); const dateB = new Date(b.data.published); return dateA > dateB ? -1 : 1;});
Fuwari添加置顶功能
https://p1ume.vercel.app/posts/fuwari/pinned/