import { useCallback, useEffect, useState } from "react"; import { adminApi } from "../../api"; import { IconKitSvg } from "../../components/IconKitSvg"; import type { AdminPromptTemplate } from "../../types"; type Notify = (type: "success" | "error" | "info", text: string) => void; // gpt-image 仅支持这 3 种成品尺寸;"" = 用各 builder 的写死默认 const RATIOS = [ { key: "", label: "默认" }, { key: "1:1", label: "1:1 方" }, { key: "portrait", label: "竖 2:3" }, { key: "landscape", label: "横 3:2" }, { key: "16:9", label: "宽 16:9" } ]; export function AdminPromptsPage({ notify }: { notify: Notify }) { const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); const [draft, setDraft] = useState>({}); const [saving, setSaving] = useState(null); const load = useCallback(async () => { setLoading(true); try { const list = await adminApi.promptTemplates(); setRows(list); setDraft(Object.fromEntries(list.map((r) => [r.id, { template: r.template, ratio: r.ratio }]))); } catch { notify("error", "加载提示词模板失败"); } finally { setLoading(false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { void load(); }, [load]); async function save(r: AdminPromptTemplate) { const d = draft[r.id]; if (!d) return; setSaving(r.id); try { const u = await adminApi.updatePromptTemplate(r.id, { template: d.template, ratio: d.ratio }); setRows((l) => l.map((x) => (x.id === r.id ? u : x))); notify("success", `已保存「${r.label}」`); } catch { notify("error", "保存失败"); } finally { setSaving(null); } } async function toggle(r: AdminPromptTemplate, enabled: boolean) { try { const u = await adminApi.updatePromptTemplate(r.id, { enabled }); setRows((l) => l.map((x) => (x.id === r.id ? u : x))); } catch { notify("error", "切换失败"); } } return ( <>

提示词

// 视频线 6 条 · 生图/视频提示词正文 + 比例可改;留空或停用 → 回落写死默认
{loading ? (

加载中…

// fetching prompt templates

) : (
{rows.map((r) => { const d = draft[r.id] || { template: r.template, ratio: r.ratio }; const dirty = d.template !== r.template || d.ratio !== r.ratio; const isVideo = r.key === "video_segment"; return (
{r.label} // {r.key}
{r.placeholders.length > 0 && (
占位符 {r.placeholders.map((p) => {`{${p}}`})} · 运行时自动替换,保留这些占位符即可
)}