Files
jiang13-bbs/frontend/components/MarkdownBody.tsx
freefire 0b60c4299b feat: 公文三栏单页布局,并修复时间线目录跳转与样式
公告/单页对齐帖子详情三栏壳;时间线按日锚点可跳转,条目改为日志行并用暖金日期区分 feat 色。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 21:15:15 +08:00

89 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { ReactNode } from "react";
import type { Components } from "react-markdown";
import { renderMarkdownToReact } from "@/components/markdown/renderMarkdown";
import { splitContentSegments } from "@/lib/directiveBlocks";
import type { HideKind } from "@/lib/hideBlocks";
import MdHideBlock from "@/components/markdown/MdHideBlock";
import MdTimeline from "@/components/markdown/MdTimeline";
type MarkdownBodyProps = {
content: string;
className?: string;
components?: Components;
/** 详情页解锁门禁需要 */
postId?: number;
/** 登录跳转(公告等无 postId 时) */
loginHref?: string;
loggedIn?: boolean;
};
/**
* 站点统一 Markdown 渲染(服务端):GFM + Shiki;支持 [hide] / [timeline]。
*/
export default async function MarkdownBody({
content,
className = "prose-content",
components,
postId,
loginHref,
loggedIn = false,
}: MarkdownBodyProps) {
const segs = splitContentSegments(content || "");
const nodes = [];
for (let i = 0; i < segs.length; i++) {
const seg = segs[i];
if (seg.type === "md") {
if (!seg.text.trim()) continue;
nodes.push(
<div key={`md-${i}`}>{await renderMarkdownToReact(seg.text, components)}</div>
);
continue;
}
if (seg.type === "timeline") {
const bodies: ReactNode[] = [];
for (let j = 0; j < seg.items.length; j++) {
const it = seg.items[j];
if (!it.body.trim()) {
bodies.push(null);
continue;
}
// 条目正文不挂目录 id,避免与块外 headingRenderer 错位
bodies.push(await renderMarkdownToReact(it.body));
}
nodes.push(
<MdTimeline
key={`tl-${i}`}
items={seg.items}
renderBody={(_it, idx) => bodies[idx]}
/>
);
continue;
}
const kind = seg.kind as HideKind;
if (seg.locked) {
nodes.push(
<MdHideBlock
key={`hide-${i}`}
kind={kind}
points={seg.points}
locked
postId={postId}
loginHref={loginHref}
loggedIn={loggedIn}
/>
);
continue;
}
const inner = seg.body.trim()
? await renderMarkdownToReact(seg.body)
: null;
nodes.push(
<MdHideBlock key={`hide-${i}`} kind={kind} points={seg.points}>
{inner}
</MdHideBlock>
);
}
return <div className={className}>{nodes}</div>;
}