v0.10.35: 原站新增内容增量导入(import-live-posts REST 19 篇 + download-missing-media 16 图)+ 博客 HTML 文章统一 single 模板布局(PostHero 白摘要/Post Views,弃旧自定义卡片版式)
This commit is contained in:
@@ -16,6 +16,7 @@ export type PostHeroData = {
|
||||
featuredAlt?: string | null;
|
||||
publishedAt?: Date | string | null;
|
||||
updatedAt?: Date | string | null;
|
||||
views?: number | null;
|
||||
};
|
||||
|
||||
/** # 函数 日期长格式(原站 October 21, 2026) **/
|
||||
@@ -51,6 +52,13 @@ export function PostHero(props: { post: PostHeroData }) {
|
||||
<span className="ds-info-text">{fmtDate(post.publishedAt)}</span>
|
||||
</li>
|
||||
) : null}
|
||||
{/* Post Views(原站 hero 内 ele-view-dif shortcode widget,取证 161855) */}
|
||||
{post.views != null && post.views > 0 ? (
|
||||
<li>
|
||||
<svg viewBox="0 0 512 512" className="ds-ico" aria-hidden><path d="M33.9 330.7H10c-2.8 0-5 2.2-5 5v80c0 2.8 2.2 5 5 5h23.9c2.8 0 5-2.2 5-5v-80c0-2.8-2.2-5-5-5zM130.7 298.7h-23.9c-2.8 0-5 2.2-5 5v112c0 2.8 2.2 5 5 5h23.9c2.8 0 5-2.2 5-5v-112c0-2.8-2.2-5-5-5zM227.6 250.7h-23.9c-2.8 0-5 2.2-5 5v160c0 2.8 2.2 5 5 5h23.9c2.8 0 5-2.2 5-5v-160c0-2.8-2.2-5-5-5zM324.4 202.7h-23.9c-2.8 0-5 2.2-5 5v208c0 2.8 2.2 5 5 5h23.9c2.8 0 5-2.2 5-5v-208c0-2.8-2.2-5-5-5zM421.3 154.7h-23.9c-2.8 0-5 2.2-5 5v256c0 2.8 2.2 5 5 5h23.9c2.8 0 5-2.2 5-5v-256c0-2.8-2.2-5-5-5zM502 61.7h-23.9c-2.8 0-5 2.2-5 5v352c0 2.8 2.2 5 5 5H502c2.8 0 5-2.2 5-5v-352c0-2.8-2.2-5-5-5z" /></svg>
|
||||
<span className="ds-info-text">Post Views: {post.views}</span>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="ds-col-r">
|
||||
@@ -83,7 +91,9 @@ export const POST_HERO_CSS = `
|
||||
.ds-posthero .ds-col-l { width: 50%; padding: 0 10px 0 30px; display: flex; flex-direction: column; justify-content: center; }
|
||||
.ds-posthero .ds-col-r { width: 50%; padding: 0 30px 0 10px; display: flex; }
|
||||
.ds-posthero .ds-title { margin: 0; font-size: 28px; font-weight: 600; line-height: 1.25; color: #fff; }
|
||||
.ds-posthero .ds-excerpt { margin: 0 0 20px; font-size: 16px; line-height: 24px; color: #333; }
|
||||
/* 摘要:原站 hero 深蓝底上为白色(161855 取证 widget 1049c63 color #fff 18px;
|
||||
* 16px 取自早期单篇取证,颜色统一按深底可读的 #fff) */
|
||||
.ds-posthero .ds-excerpt { margin: 0 0 20px; font-size: 16px; line-height: 24px; color: #fff; }
|
||||
.ds-posthero .ds-info { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; }
|
||||
.ds-posthero .ds-info li { display: flex; align-items: center; margin: 0 10px; font-size: 16px; line-height: 24px; }
|
||||
.ds-posthero .ds-ico { width: 16px; height: 16px; fill: #fff; margin-right: 5px; }
|
||||
|
||||
+14
-87
@@ -9,7 +9,6 @@ import { ElementorRenderer, extractElementor } from "~/components/elementor/Rend
|
||||
import { InquiryForm } from "~/components/website/InquiryForm";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "~/components/ui/accordion";
|
||||
import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator } from "~/components/ui/breadcrumb";
|
||||
import { getPublishedContent, getMenuTree, listPosts, type MenuNode, type PostListItem } from "~/server/data";
|
||||
import { PostHero } from "~/components/website/PostHero";
|
||||
import { resolveImageVariants } from "~/utils/image-variant";
|
||||
@@ -141,7 +140,6 @@ function BlogPostPage() {
|
||||
);
|
||||
}
|
||||
const post = data.post;
|
||||
const cats = post.terms.filter((t) => t.taxonomy === "category");
|
||||
const elementor = extractElementor(post.meta);
|
||||
// Elementor 文章(原站 single 模板):hero + 面包屑条 + 三列正文
|
||||
// (cd804a1 取证 2026-11-03:分享栏 60px / 正文 776px / 侧栏 360px,容器 1200)
|
||||
@@ -166,41 +164,19 @@ function BlogPostPage() {
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
// HTML 文章:从正文提取 h2/h3 生成目录(并注入锚点 id)
|
||||
// HTML 文章(无 elementor 的 dump 文章 / REST 增量导入):与原站 single 模板同布局
|
||||
// (hero + 面包屑 + 分享栏/正文/侧栏 三列),正文走 content-html(WP 渲染 HTML)
|
||||
const toc = post.html ? buildToc(post.html) : null;
|
||||
return (
|
||||
<Layout layout={data.layout}>
|
||||
<div className="site-container">
|
||||
<Breadcrumb aria-label="Breadcrumb">
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem><BreadcrumbLink to="/">Home</BreadcrumbLink></BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem><BreadcrumbLink to="/blog/">Blog</BreadcrumbLink></BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem><BreadcrumbPage>{post.title}</BreadcrumbPage></BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<div className="grid grid-cols-1 items-start gap-7 min-[1080px]:grid-cols-[1fr_340px]">
|
||||
{/* 主栏:白卡正文(目录 + 内容) */}
|
||||
<article className="rounded-lg bg-white p-6 min-[760px]:p-9">
|
||||
<h1 className="mt-0 text-[30px] leading-[1.35]">{post.title}</h1>
|
||||
<div className="mb-5 text-[13px] text-muted-foreground">
|
||||
{post.publishedAt ? new Date(post.publishedAt).toISOString().slice(0, 10) : ""}
|
||||
{cats.length > 0 ? (
|
||||
<>
|
||||
{" · "}
|
||||
{cats.map((c, i) => (
|
||||
<span key={c.slug}>
|
||||
{i > 0 ? ", " : ""}
|
||||
{c.name}
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
{post.featuredUrl ? (
|
||||
<img src={post.featuredUrl} alt={post.featuredAlt || post.title} className="my-3 mb-5 w-full rounded-lg" />
|
||||
) : null}
|
||||
<PostHero post={post} />
|
||||
<style>{POST_LAYOUT_CSS}</style>
|
||||
<div className="ds-postlayout">
|
||||
<div className="ds-wrap">
|
||||
<div className="ds-rail">
|
||||
<ShareRail />
|
||||
</div>
|
||||
<div className="ds-main">
|
||||
{toc && toc.items.length >= 3 ? (
|
||||
<Accordion type="single" collapsible defaultValue="toc" className="mb-6 rounded-md border px-4">
|
||||
<AccordionItem value="toc" className="border-b">
|
||||
@@ -219,65 +195,16 @@ function BlogPostPage() {
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
) : null}
|
||||
{elementor ? (
|
||||
<ElementorRenderer doc={elementor} />
|
||||
) : post.html ? (
|
||||
{post.html ? (
|
||||
<div className="content-html" dangerouslySetInnerHTML={{ __html: toc?.html || post.html }} />
|
||||
) : (
|
||||
<p className="text-muted-foreground">{post.excerpt || t("blog_importing")}</p>
|
||||
)}
|
||||
</article>
|
||||
|
||||
{/* 侧栏:社交图标 / CONTACT US 表单 / IoT Gateways 产品分组 */}
|
||||
<aside className="space-y-5">
|
||||
<div className="flex justify-center gap-3 rounded-lg bg-white p-4">
|
||||
<BrandIcon label="LinkedIn" bg="#0a66c2" path="M20.45 20.45h-3.55v-5.57c0-1.33-.03-3.04-1.85-3.04-1.86 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05c.47-.9 1.63-1.85 3.36-1.85 3.6 0 4.27 2.37 4.27 5.45v6.29zM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12zM7.12 20.45H3.56V9h3.56v11.45z" />
|
||||
<BrandIcon label="YouTube" bg="#ff0000" path="M23.5 6.19a3.02 3.02 0 0 0-2.12-2.14C19.5 3.55 12 3.55 12 3.55s-7.5 0-9.38.5A3.02 3.02 0 0 0 .5 6.19C0 8.07 0 12 0 12s0 3.93.5 5.81a3.02 3.02 0 0 0 2.12 2.14c1.88.5 9.38.5 9.38.5s7.5 0 9.38-.5a3.02 3.02 0 0 0 2.12-2.14C24 15.93 24 12 24 12s0-3.93-.5-5.81zM9.55 15.57V8.43L15.82 12l-6.27 3.57z" />
|
||||
<BrandIcon label="Facebook" bg="#1877f2" path="M24 12.07C24 5.4 18.63 0 12 0S0 5.4 0 12.07C0 18.1 4.39 23.09 10.13 24v-8.44H7.08v-3.49h3.05V9.41c0-3.02 1.79-4.7 4.53-4.7 1.31 0 2.68.24 2.68.24v2.97h-1.51c-1.49 0-1.95.93-1.95 1.89v2.26h3.32l-.53 3.49h-2.79V24C19.61 23.09 24 18.1 24 12.07z" />
|
||||
</div>
|
||||
<div className="rounded-lg bg-white p-5">
|
||||
<InquiryForm formKey="contact-us-form-sidebar" productInterest={post.title} title={t("blog_contact_title")} />
|
||||
</div>
|
||||
{data.gwGroups.length > 0 ? (
|
||||
<div className="rounded-lg bg-white p-5">
|
||||
<h3 className="mt-0 text-[20px] font-bold text-brand">{t("blog_gateways")}</h3>
|
||||
<Accordion type="multiple" className="border-t">
|
||||
{data.gwGroups.map((g) => (
|
||||
<AccordionItem key={g.id} value={`gw-${g.id}`}>
|
||||
<AccordionTrigger className="text-[15px] font-medium">{g.title}</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<ul className="m-0 list-none p-0">
|
||||
{g.children.map((c) => (
|
||||
<li key={c.id} className="py-1">
|
||||
<a href={c.url} className="text-[14px] text-muted-foreground hover:text-brand hover:underline">{c.title}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<aside className="ds-side">
|
||||
<PostSidebarWidget groups={data.gwGroups} productInterest={post.title} />
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
{/* 底部 Related Posts(原站:4 列卡片,灰渐变占位 + 标题) */}
|
||||
{data.related.length > 0 ? (
|
||||
<section className="my-12">
|
||||
<h2 className="mb-6 text-center text-[26px]">{t("blog_related_posts")}</h2>
|
||||
<div className="grid grid-cols-2 gap-5 min-[960px]:grid-cols-4">
|
||||
{data.related.map((p) => (
|
||||
<Link key={p.id} to="/blog/$slug/" params={{ slug: p.slug }} className="block">
|
||||
<div className="mb-3 aspect-[4/3] overflow-hidden rounded-md bg-gradient-to-b from-[#9aa3ad] to-[#c9ced4]">
|
||||
{p.imageUrl ? <img src={p.imageUrl} alt={p.title} loading="lazy" className="h-full w-full object-cover" /> : null}
|
||||
</div>
|
||||
<div className="text-[15px] leading-[1.45] font-medium hover:text-brand">{p.title}</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user