feat: 站点单页、时间线导入与行级 BBCode 短代码
首页右栏接入单页入口与公告置顶/全部页;Markdown 编辑器支持可视化时间线与 Git 导入;隐藏块改为 [hide]/[timeline] 命名闭合,并修复单页/公告 GORM 更新只改 updated_at 的问题。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
67
frontend/components/markdown/MdTimeline.tsx
Normal file
67
frontend/components/markdown/MdTimeline.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import {
|
||||
groupTimelineByDate,
|
||||
type TimelineItem,
|
||||
} from "@/lib/timelineBlocks";
|
||||
|
||||
type MdTimelineProps = {
|
||||
items: TimelineItem[];
|
||||
/** 每项正文已渲染的 React 节点(与 items 等长);缺省则只显示纯文本 */
|
||||
renderBody?: (item: TimelineItem, index: number) => ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* 按日分组的垂直时间线(更新树)。颜色走 CSS 令牌。
|
||||
*/
|
||||
export default function MdTimeline({ items, renderBody }: MdTimelineProps) {
|
||||
if (!items.length) {
|
||||
return <p className="meta px-1 py-2">暂无时间线条目</p>;
|
||||
}
|
||||
const groups = groupTimelineByDate(items);
|
||||
let flat = 0;
|
||||
|
||||
return (
|
||||
<div className="j13-timeline" role="list" aria-label="时间线">
|
||||
{groups.map((g) => (
|
||||
<div key={g.date} className="j13-timeline-day">
|
||||
<div className="j13-timeline-day-label">
|
||||
<time dateTime={g.date}>{g.date}</time>
|
||||
</div>
|
||||
<ul className="j13-timeline-items">
|
||||
{g.items.map((it) => {
|
||||
const idx = flat++;
|
||||
return (
|
||||
<li key={`${g.date}-${idx}-${it.title}`} className="j13-timeline-item" role="listitem">
|
||||
<span className="j13-timeline-dot" aria-hidden />
|
||||
<div className="j13-timeline-card">
|
||||
<div className="j13-timeline-head">
|
||||
<h3 className="j13-timeline-title">{it.title}</h3>
|
||||
{it.sourceUrl ? (
|
||||
<a
|
||||
href={it.sourceUrl}
|
||||
className="j13-timeline-src"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
data-tip="查看提交"
|
||||
aria-label="查看提交"
|
||||
>
|
||||
<ExternalLink size={13} />
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
{renderBody ? (
|
||||
<div className="j13-timeline-body">{renderBody(it, idx)}</div>
|
||||
) : it.body.trim() ? (
|
||||
<div className="j13-timeline-body whitespace-pre-wrap">{it.body}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user