feat: 代码片段功能与 Markdown 内嵌片段渲染、文档截图等
This commit is contained in:
111
frontend/app/snippets/[id]/edit/page.tsx
Normal file
111
frontend/app/snippets/[id]/edit/page.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
"use client";
|
||||
|
||||
import { Suspense, useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { apiMe, apiGetSnippet } from "@/lib/api";
|
||||
import SnippetEditor from "@/components/SnippetEditor";
|
||||
|
||||
function EditSnippetInner({ id }: { id: number }) {
|
||||
const router = useRouter();
|
||||
const [ready, setReady] = useState(false);
|
||||
const [snippet, setSnippet] = useState<{
|
||||
title: string;
|
||||
language: string;
|
||||
description: string;
|
||||
code: string;
|
||||
visibility: "public" | "private";
|
||||
user_id: number;
|
||||
} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
(async () => {
|
||||
const me = await apiMe().catch(() => null);
|
||||
if (cancelled) return;
|
||||
if (!me?.user) {
|
||||
router.push(`/login?redirect=/snippets/${id}/edit`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = await apiGetSnippet(id);
|
||||
if (cancelled) return;
|
||||
if (!data) {
|
||||
router.push("/snippets");
|
||||
return;
|
||||
}
|
||||
// 仅 owner 或 staff 可编辑(后端会再次校验)
|
||||
setSnippet({
|
||||
title: data.snippet.title,
|
||||
language: data.snippet.language,
|
||||
description: data.snippet.description,
|
||||
code: data.snippet.code,
|
||||
visibility: data.snippet.visibility,
|
||||
user_id: data.snippet.user_id,
|
||||
});
|
||||
setReady(true);
|
||||
} catch {
|
||||
if (!cancelled) router.push("/snippets");
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [router, id]);
|
||||
|
||||
if (!ready || !snippet) {
|
||||
return (
|
||||
<div className="j13-compose-stage is-studio px-8 py-16 text-center">
|
||||
<p className="meta">正在加载片段…</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SnippetEditor
|
||||
mode="edit"
|
||||
snippetId={id}
|
||||
initialTitle={snippet.title}
|
||||
initialLanguage={snippet.language}
|
||||
initialDescription={snippet.description}
|
||||
initialCode={snippet.code}
|
||||
initialVisibility={snippet.visibility}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function EditSnippetPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>;
|
||||
}) {
|
||||
const [id, setId] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
params.then((p) => {
|
||||
const numId = Number(p.id);
|
||||
if (Number.isInteger(numId) && numId > 0) setId(numId);
|
||||
});
|
||||
}, [params]);
|
||||
|
||||
if (id === null) {
|
||||
return (
|
||||
<div className="j13-compose-stage is-studio px-8 py-16 text-center">
|
||||
<p className="meta">正在加载…</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="j13-compose-page">
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="j13-compose-stage is-studio px-8 py-16 text-center">
|
||||
<p className="meta">正在加载片段…</p>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<EditSnippetInner id={id} />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user