feat: 站点单页、时间线导入与行级 BBCode 短代码

首页右栏接入单页入口与公告置顶/全部页;Markdown 编辑器支持可视化时间线与 Git 导入;隐藏块改为 [hide]/[timeline] 命名闭合,并修复单页/公告 GORM 更新只改 updated_at 的问题。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-17 19:52:06 +08:00
parent 2aacf99a49
commit a83276060f
52 changed files with 5073 additions and 648 deletions

View File

@@ -0,0 +1,54 @@
"use client";
import { useEffect, useState } from "react";
import { Loader2 } from "lucide-react";
import { apiAdminListSitePages, type SitePage } from "@/lib/api";
import PageComposeClient from "../../PageComposeClient";
/** 无详情 API:从管理列表按 id 拉取后进入编辑器 */
export default function PageEditLoader({ id }: { id: number }) {
const [item, setItem] = useState<SitePage | null>(null);
const [error, setError] = useState("");
const [loading, setLoading] = useState(true);
useEffect(() => {
let alive = true;
setLoading(true);
apiAdminListSitePages()
.then((res) => {
if (!alive) return;
const found = res.pages.find((p) => p.id === id) ?? null;
if (!found) {
setError("页面不存在或已删除");
setItem(null);
} else {
setItem(found);
setError("");
}
})
.catch((e) => {
if (!alive) return;
setError(e instanceof Error ? e.message : "加载失败");
})
.finally(() => {
if (alive) setLoading(false);
});
return () => {
alive = false;
};
}, [id]);
if (loading) {
return (
<div className="panel p-10 flex items-center justify-center gap-2 meta">
<Loader2 size={18} className="animate-spin" /> 加载页面…
</div>
);
}
if (error || !item) {
return <p className="alert-error">{error || "页面不存在"}</p>;
}
return <PageComposeClient mode="edit" initial={item} />;
}