diff --git a/app/(admin)/settings/admin-settings-client.tsx b/app/(admin)/settings/admin-settings-client.tsx index 0cda518f2..6665f882b 100644 --- a/app/(admin)/settings/admin-settings-client.tsx +++ b/app/(admin)/settings/admin-settings-client.tsx @@ -89,11 +89,24 @@ import { normalizeDulupayConfig, type DulupayConfig, } from "@/lib/payment/dulupay-config"; +import { + DEFAULT_CANVAS_SKILL_TAGS, + normalizeCanvasSkillTags, + type CanvasSkillTag, +} from "@/lib/canvas-skill-tags"; const fetcher = (url: string) => fetch(url).then((res) => res.json()); const DEFAULT_RUNWARE_REALTIME_MODEL = "runware:101@1"; const DEFAULT_RUNWARE_STYLIZE_MODEL = "runware:106@1"; +function createEmptyCanvasSkillTag(): CanvasSkillTag { + return { + id: `tag-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`, + name: "", + prompt: "", + }; +} + // 类型定义 type User = { id: string; @@ -982,6 +995,12 @@ export function AdminSettingsClient() { // 快捷短语状态 const [questions, setQuestions] = useState([]); const { data: questionsData, mutate: mutateQuestions } = useSWR("/api/starter-questions", fetcher); + const [canvasSkillTags, setCanvasSkillTags] = useState( + DEFAULT_CANVAS_SKILL_TAGS + ); + const { data: canvasSkillTagsData, mutate: mutateCanvasSkillTags } = useSWR<{ + tags?: CanvasSkillTag[]; + }>("/api/canvas-skill-tags", fetcher); // 系统通知状态 const [notification, setNotification] = useState<{ enabled: boolean; title: string; content: string }>({ @@ -1022,6 +1041,12 @@ export function AdminSettingsClient() { } }, [questionsData]); + useEffect(() => { + if (canvasSkillTagsData) { + setCanvasSkillTags(normalizeCanvasSkillTags(canvasSkillTagsData.tags)); + } + }, [canvasSkillTagsData]); + const handleQuestionChange = (index: number, value: string) => { const newQuestions = [...questions]; newQuestions[index] = value; @@ -1052,6 +1077,45 @@ export function AdminSettingsClient() { } }; + const handleCanvasSkillTagChange = ( + index: number, + field: "name" | "prompt", + value: string + ) => { + setCanvasSkillTags((prev) => + prev.map((item, itemIndex) => + itemIndex === index ? { ...item, [field]: value } : item + ) + ); + }; + + const handleAddCanvasSkillTag = () => { + setCanvasSkillTags((prev) => [...prev, createEmptyCanvasSkillTag()]); + }; + + const handleRemoveCanvasSkillTag = (index: number) => { + setCanvasSkillTags((prev) => prev.filter((_, itemIndex) => itemIndex !== index)); + }; + + const handleSaveCanvasSkillTags = async () => { + try { + const payload = normalizeCanvasSkillTags(canvasSkillTags); + const res = await fetch("/api/canvas-skill-tags", { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ tags: payload }), + }); + if (!res.ok) throw new Error("Failed"); + + const data = await res.json().catch(() => ({})); + setCanvasSkillTags(normalizeCanvasSkillTags(data?.tags ?? payload)); + toast.success("快捷标签已更新"); + mutateCanvasSkillTags(); + } catch (error) { + toast.error("快捷标签更新失败"); + } + }; + // 认证设置状态 const [authSettings, setAuthSettings] = useState({ dingTalkAppKey: "", @@ -5597,37 +5661,85 @@ export function AdminSettingsClient() { - - - 快捷短语 - 配置聊天输入框下方的预设快捷提问 - - - {questions.map((q, index) => ( -
- handleQuestionChange(index, e.target.value)} - placeholder={`问题 ${index + 1}`} - /> - -
- ))} - - -
-
+
+ + + 快捷短语 + 配置聊天输入框下方的预设快捷提问 + + + {questions.map((q, index) => ( +
+ handleQuestionChange(index, e.target.value)} + placeholder={`问题 ${index + 1}`} + /> + +
+ ))} + + +
+
+ + + + 画布对话快捷标签 + + 每个标签包含「标签名 + 预设提示词」。用户点击标签后,发送消息时会自动附带对应提示词。 + + + + {canvasSkillTags.map((tag, index) => ( +
+
+ + handleCanvasSkillTagChange(index, "name", event.target.value) + } + placeholder={`标签名称 ${index + 1}`} + /> + +
+