阅读进度 + 继续阅读 + 文章系列 + 完整 SEO
This commit is contained in:
1 parent
336dcc7670
commit
6ac59f404b
9 files changed
+424
-4
No files matched your search
@@ -0,0 +1,49 @@
|
||||
---
|
||||
import type { BlogPost } from '@/lib/posts';
|
||||
import { siteConfig } from '../../site.config.mjs';
|
||||
|
||||
interface Props {
|
||||
currentPost: BlogPost;
|
||||
posts: BlogPost[];
|
||||
}
|
||||
|
||||
const { currentPost, posts } = Astro.props;
|
||||
const currentIndex = posts.findIndex((post) => post.slug === currentPost.slug);
|
||||
const previousPost = currentIndex > 0 ? posts[currentIndex - 1] : undefined;
|
||||
const nextPost = currentIndex >= 0 && currentIndex < posts.length - 1 ? posts[currentIndex + 1] : undefined;
|
||||
const labels = siteConfig.series?.labels ?? {};
|
||||
const seriesKey = currentPost.series ?? '';
|
||||
const seriesTitle = labels[seriesKey] ?? seriesKey;
|
||||
---
|
||||
|
||||
<section class="post-series" aria-labelledby="post-series-title">
|
||||
<header class="post-series-head">
|
||||
<div>
|
||||
<span>ARTICLE SERIES</span>
|
||||
<h2 id="post-series-title">{seriesTitle}</h2>
|
||||
</div>
|
||||
<strong>第 {currentIndex + 1} / {posts.length} 篇</strong>
|
||||
</header>
|
||||
|
||||
<ol class="post-series-list">
|
||||
{posts.map((post, index) => (
|
||||
<li class:list={{ 'is-current': post.slug === currentPost.slug }}>
|
||||
<a href={post.href} aria-current={post.slug === currentPost.slug ? 'page' : undefined}>
|
||||
<span>{String(index + 1).padStart(2, '0')}</span>
|
||||
<strong>{post.title}</strong>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
{(previousPost || nextPost) && (
|
||||
<nav class="post-series-nav" aria-label="系列文章导航">
|
||||
{previousPost ? (
|
||||
<a href={previousPost.href} rel="prev"><span>上一篇</span><strong>{previousPost.title}</strong></a>
|
||||
) : <span></span>}
|
||||
{nextPost ? (
|
||||
<a href={nextPost.href} rel="next"><span>下一篇</span><strong>{nextPost.title}</strong></a>
|
||||
) : <span></span>}
|
||||
</nav>
|
||||
)}
|
||||
</section>
|
||||
@@ -6,6 +6,7 @@ const posts = defineCollection({
|
||||
schema: z.object({
|
||||
title: z.any().optional(),
|
||||
date: z.any().optional(),
|
||||
updated: z.any().optional(),
|
||||
abbrlink: z.any().optional(),
|
||||
tags: z.any().optional(),
|
||||
category: z.any().optional(),
|
||||
@@ -14,6 +15,8 @@ const posts = defineCollection({
|
||||
description: z.any().optional(),
|
||||
comments: z.any().optional(),
|
||||
cover: z.any().optional(),
|
||||
series: z.any().optional(),
|
||||
seriesOrder: z.any().optional(),
|
||||
}).passthrough(),
|
||||
});
|
||||
|
||||
|
||||
@@ -7,11 +7,31 @@ import { publicSiteConfig, siteConfig } from '../../site.config.mjs';
|
||||
interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
canonicalPath?: string;
|
||||
image?: string;
|
||||
type?: 'website' | 'article';
|
||||
publishedTime?: string;
|
||||
modifiedTime?: string;
|
||||
keywords?: string[];
|
||||
jsonLd?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const siteTitle = siteConfig.site.name;
|
||||
const { title = siteTitle, description = siteConfig.site.description } = Astro.props;
|
||||
const {
|
||||
title = siteTitle,
|
||||
description = siteConfig.site.description,
|
||||
canonicalPath = Astro.url.pathname,
|
||||
image = siteConfig.author.avatar,
|
||||
type = 'website',
|
||||
publishedTime,
|
||||
modifiedTime,
|
||||
keywords = [],
|
||||
jsonLd,
|
||||
} = Astro.props;
|
||||
const pageTitle = title === siteTitle ? siteTitle : `${title} | ${siteTitle}`;
|
||||
const canonicalUrl = new URL(canonicalPath, siteConfig.site.url).href;
|
||||
const socialImageUrl = new URL(image, siteConfig.site.url).href;
|
||||
const jsonLdHtml = jsonLd ? JSON.stringify(jsonLd).replace(/</g, '\\u003c') : '';
|
||||
const currentYear = new Date().getFullYear();
|
||||
const publicConfigJson = JSON.stringify(publicSiteConfig).replace(/</g, '\\u003c');
|
||||
const consoleSites = [
|
||||
@@ -48,6 +68,27 @@ const consoleSites = [
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content={description} />
|
||||
<meta name="author" content={siteConfig.author.name} />
|
||||
{keywords.length > 0 && <meta name="keywords" content={keywords.join(', ')} />}
|
||||
<link rel="canonical" href={canonicalUrl} />
|
||||
<link rel="alternate" type="application/rss+xml" title={`${siteTitle} RSS`} href={new URL('/rss.xml', siteConfig.site.url).href} />
|
||||
<meta property="og:site_name" content={siteTitle} />
|
||||
<meta property="og:locale" content="zh_CN" />
|
||||
<meta property="og:type" content={type} />
|
||||
<meta property="og:title" content={pageTitle} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:url" content={canonicalUrl} />
|
||||
<meta property="og:image" content={socialImageUrl} />
|
||||
<meta property="og:image:alt" content={title} />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content={pageTitle} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
<meta name="twitter:image" content={socialImageUrl} />
|
||||
{publishedTime && <meta property="article:published_time" content={publishedTime} />}
|
||||
{modifiedTime && <meta property="article:modified_time" content={modifiedTime} />}
|
||||
{type === 'article' && <meta property="article:author" content={siteConfig.author.name} />}
|
||||
{keywords.map((keyword) => <meta property="article:tag" content={keyword} />)}
|
||||
{jsonLd && <script type="application/ld+json" is:inline set:html={jsonLdHtml}></script>}
|
||||
<link rel="icon" href={siteConfig.assets.icon} />
|
||||
<script defer src={siteConfig.analytics.umami.script} data-website-id={siteConfig.analytics.umami.websiteId}></script>
|
||||
<script is:inline set:html={`window.SITE_CONFIG = ${publicConfigJson};`}></script>
|
||||
|
||||
Loaded 3 of 9 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user