/** * Schema UI 表单组件 * * 按 ui_entry.schema.fields 渲染表单,并按 ui_entry.submit 契约提交。 * 只允许使用后端 ui_entry.submit 指定的提交契约,不能由前端猜 endpoint。 * 使用 console 语义 token 保持视觉一致。 */ import { useState } from 'react'; import { AlertCircle, Loader2, CheckCircle } from 'lucide-react'; import { UISchemaDefinition, UISubmitContract } from '@typings/plugin'; import { Button } from '@components/ui/button'; import { Input } from '@components/ui/input'; import { Label } from '@components/ui/label'; interface SchemaPluginFormProps { pluginName: string; schema: UISchemaDefinition; submit: UISubmitContract; } export function SchemaPluginForm({ pluginName: _pluginName, schema, submit }: SchemaPluginFormProps) { const [formData, setFormData] = useState>({}); const [isSubmitting, setIsSubmitting] = useState(false); const [result, setResult] = useState(null); const [error, setError] = useState(null); const handleFieldChange = (name: string, value: any) => { setFormData((prev) => ({ ...prev, [name]: value })); }; const handleSubmit = async () => { setIsSubmitting(true); setError(null); setResult(null); try { // 只使用 ui_entry.submit 契约 if (submit.method !== 'POST') { throw new Error(`不支持的提交方法: ${submit.method}`); } const response = await fetch(submit.path, { method: submit.method, headers: { 'Content-Type': submit.content_type || 'application/json', }, body: JSON.stringify(formData), }); if (!response.ok) { const errorData = await response.json().catch(() => null); const errorMessage = errorData?.message || errorData?.detail || `请求失败: ${response.status}`; throw new Error(errorMessage); } const data = await response.json(); // 按 success_path 读取结果 if (submit.success_path) { const successResult = submit.success_path.split('.').reduce((obj, key) => obj?.[key], data); setResult(successResult); } else { setResult(data); } } catch (err: any) { setError(err.message || '提交失败'); } finally { setIsSubmitting(false); } }; return (
{/* 表单标题 */}

{schema.title}

{schema.description && (

{schema.description}

)}
{/* 表单字段 */}

参数

{schema.fields.map((field) => (
{field.type === 'string' && field.name.includes('content') ? (