// ============================================================ // CreateScenarioModal — 创建自建情景对话框 // 职责:提供表单让用户创建新的自定义情景 // ============================================================ import { useCallback, useEffect, useRef, useState } from "react"; import { useI18n } from "../../lib/i18n"; import type { CreateScenarioRequest } from "../../lib/api/scenarios"; interface CreateScenarioModalProps { onClose: () => void; onSubmit: (data: CreateScenarioRequest) => Promise; } // 预设常用图标 const PRESET_ICONS = [ "✨", "🎭", "🎨", "🎯", "🎪", "🎬", "📖", "📚", "📝", "📋", "📌", "📍", "🔬", "🔭", "🔮", "💡", "💼", "💻", "🎓", "🎤", "🎵", "🎸", "🎹", "🎺", ]; export function CreateScenarioModal({ onClose, onSubmit }: CreateScenarioModalProps) { const { t } = useI18n(); const [name, setName] = useState(""); const [icon, setIcon] = useState("✨"); const [description, setDescription] = useState(""); const [prompt, setPrompt] = useState(""); const [greeting, setGreeting] = useState(""); const [language, setLanguage] = useState("zh-CN"); const [error, setError] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [showGuide, setShowGuide] = useState(false); const overlayRef = useRef(null); // 点击遮罩关闭 const handleOverlayClick = useCallback( (e: React.MouseEvent) => { if (e.target === overlayRef.current) onClose(); }, [onClose] ); // ESC 关闭 useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [onClose]); // 阻止 body 滚动 useEffect(() => { const prev = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = prev; }; }, []); const handleSubmit = useCallback( async (e: React.FormEvent) => { e.preventDefault(); setError(""); // 表单验证 if (name.length < 2 || name.length > 50) { setError(t("scenario.error.nameLength")); return; } if (prompt.length < 10 || prompt.length > 2000) { setError(t("scenario.error.promptLength")); return; } if (greeting && greeting.length > 500) { setError(t("scenario.error.greetingLength")); return; } setIsSubmitting(true); try { await onSubmit({ name, icon, description: description || undefined, prompt, greeting: greeting || undefined, language, }); onClose(); } catch (err) { setError(err instanceof Error ? err.message : "Unknown error"); } finally { setIsSubmitting(false); } }, [name, icon, description, prompt, greeting, language, onSubmit, onClose, t] ); return (
e.stopPropagation()}>
{t("scenario.create.title")}
{/* 名称 */}
setName(e.target.value)} placeholder={t("scenario.create.namePlaceholder")} maxLength={50} required /> {name.length}/50
{/* 图标选择 */}
{PRESET_ICONS.map((ic) => ( ))}
{/* 描述 */}
setDescription(e.target.value)} placeholder={t("scenario.create.descriptionPlaceholder")} maxLength={100} /> {description.length}/100
{/* Prompt */}
{showGuide && (

{t("scenario.create.promptGuide.tips")}

  • {t("scenario.create.promptGuide.tip1")}
  • {t("scenario.create.promptGuide.tip2")}
  • {t("scenario.create.promptGuide.tip3")}

{t("scenario.create.promptGuide.example")}

{`你是一位创意写作导师。
帮助用户构思故事情节、人物设定和写作技巧。

【角色定位】
- 你是导师,不是代笔人
- 激发用户创意,不直接给答案

【交互规则】
1. 提出启发性问题
2. 给出具体、可操作的建议
3. 回答控制在3-5句话`}
                
)}