新增内容引擎文章发布接口与详情页
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { articleUrl, getPublishedArticle } from '@/lib/articles';
|
||||
|
||||
const siteUrl = (process.env.NEXT_PUBLIC_SITE_URL || process.env.PUBLIC_SITE_URL || 'https://www.aiameiba.com').replace(/\/$/, '');
|
||||
|
||||
const formatDate = (value: string) => {
|
||||
const date = new Date(value);
|
||||
return Number.isNaN(date.getTime()) ? value : date.toLocaleDateString('zh-CN');
|
||||
};
|
||||
|
||||
const paragraphs = (body: string) => body.split(/\n\s*\n/).map(item => item.trim()).filter(Boolean);
|
||||
|
||||
export async function generateMetadata({ params }: { params: { identifier: string } }): Promise<Metadata> {
|
||||
const article = await getPublishedArticle(params.identifier);
|
||||
if (!article) return { title: '文章不存在' };
|
||||
const localUrl = `${siteUrl}${articleUrl(article)}`;
|
||||
const canonical = article.canonical_url || localUrl;
|
||||
return {
|
||||
title: article.title,
|
||||
description: article.summary,
|
||||
alternates: { canonical },
|
||||
openGraph: {
|
||||
type: 'article',
|
||||
title: article.title,
|
||||
description: article.summary,
|
||||
url: canonical,
|
||||
images: article.cover_image_url ? [{ url: article.cover_image_url }] : undefined,
|
||||
publishedTime: article.published_at,
|
||||
modifiedTime: article.updated_at,
|
||||
authors: [article.author_name],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function KnowledgeArticlePage({ params }: { params: { identifier: string } }) {
|
||||
const article = await getPublishedArticle(params.identifier);
|
||||
if (!article) notFound();
|
||||
const localUrl = `${siteUrl}${articleUrl(article)}`;
|
||||
const canonical = article.canonical_url || localUrl;
|
||||
const jsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Article',
|
||||
headline: article.title,
|
||||
description: article.summary,
|
||||
url: canonical,
|
||||
mainEntityOfPage: localUrl,
|
||||
author: { '@type': 'Organization', name: article.author_name },
|
||||
publisher: { '@type': 'Organization', name: '阿美巴AI', url: siteUrl },
|
||||
datePublished: article.published_at,
|
||||
dateModified: article.updated_at,
|
||||
image: article.cover_image_url || undefined,
|
||||
articleSection: article.category,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-narrow">
|
||||
<nav className="breadcrumb" aria-label="面包屑导航">
|
||||
<ol><li><a href="/">首页</a></li><li><a href="/knowledge">知识中心</a></li><li><span aria-current="page">{article.title}</span></li></ol>
|
||||
</nav>
|
||||
<article className="knowledge-article">
|
||||
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
|
||||
<header className="knowledge-article-header">
|
||||
<p className="article-meta"><span>{article.category}</span><span>{article.author_name}</span><time dateTime={article.published_at}>发布于 {formatDate(article.published_at)}</time><time dateTime={article.updated_at}>更新于 {formatDate(article.updated_at)}</time></p>
|
||||
<h1>{article.title}</h1>
|
||||
{article.summary && <p className="knowledge-article-summary">{article.summary}</p>}
|
||||
</header>
|
||||
<div className="knowledge-article-body">
|
||||
{paragraphs(article.body_plain).map((paragraph, index) => <p key={index}>{paragraph}</p>)}
|
||||
</div>
|
||||
<footer className="knowledge-article-footer"><a href="/knowledge">← 返回知识中心</a></footer>
|
||||
</article>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user