mirror of https://github.com/Mai-with-u/MaiBot.git
feat(dashboard): create DynamicConfigForm component with Hook integration
- Accept schema, values, onChange, hooks props - Check for field hooks, use if present (replace/wrapper modes) - Fall back to DynamicField for non-hooked fields - Support nested schema rendering with recursive calls - Export DynamicConfigForm and DynamicField from index Groups with Task 5. Completes Task 6 of webui-config-visualization-refactor plan.pull/1496/head
parent
4e5d091417
commit
449e2f470c
|
|
@ -0,0 +1,114 @@
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
import type { ConfigSchema, FieldSchema } from '@/types/config-schema'
|
||||||
|
import { fieldHooks, type FieldHookRegistry } from '@/lib/field-hooks'
|
||||||
|
|
||||||
|
import { DynamicField } from './DynamicField'
|
||||||
|
|
||||||
|
export interface DynamicConfigFormProps {
|
||||||
|
schema: ConfigSchema
|
||||||
|
values: Record<string, unknown>
|
||||||
|
onChange: (field: string, value: unknown) => void
|
||||||
|
hooks?: FieldHookRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DynamicConfigForm - 动态配置表单组件
|
||||||
|
*
|
||||||
|
* 根据 ConfigSchema 渲染表单字段,支持:
|
||||||
|
* 1. Hook 系统:通过 FieldHookRegistry 自定义字段渲染
|
||||||
|
* - replace 模式:完全替换默认渲染
|
||||||
|
* - wrapper 模式:包装默认渲染(通过 children 传递)
|
||||||
|
* 2. 嵌套 schema:递归渲染 schema.nested 中的子配置
|
||||||
|
* 3. 默认渲染:使用 DynamicField 组件
|
||||||
|
*/
|
||||||
|
export const DynamicConfigForm: React.FC<DynamicConfigFormProps> = ({
|
||||||
|
schema,
|
||||||
|
values,
|
||||||
|
onChange,
|
||||||
|
hooks = fieldHooks, // 默认使用全局单例
|
||||||
|
}) => {
|
||||||
|
/**
|
||||||
|
* 渲染单个字段
|
||||||
|
* 检查是否有注册的 Hook,根据 Hook 类型选择渲染方式
|
||||||
|
*/
|
||||||
|
const renderField = (field: FieldSchema) => {
|
||||||
|
const fieldPath = field.name
|
||||||
|
|
||||||
|
// 检查是否有注册的 Hook
|
||||||
|
if (hooks.has(fieldPath)) {
|
||||||
|
const hookEntry = hooks.get(fieldPath)
|
||||||
|
if (!hookEntry) return null // Type guard(理论上不会发生)
|
||||||
|
|
||||||
|
const HookComponent = hookEntry.component
|
||||||
|
|
||||||
|
if (hookEntry.type === 'replace') {
|
||||||
|
// replace 模式:完全替换默认渲染
|
||||||
|
return (
|
||||||
|
<HookComponent
|
||||||
|
fieldPath={fieldPath}
|
||||||
|
value={values[field.name]}
|
||||||
|
onChange={(v) => onChange(field.name, v)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// wrapper 模式:包装默认渲染
|
||||||
|
return (
|
||||||
|
<HookComponent
|
||||||
|
fieldPath={fieldPath}
|
||||||
|
value={values[field.name]}
|
||||||
|
onChange={(v) => onChange(field.name, v)}
|
||||||
|
>
|
||||||
|
<DynamicField
|
||||||
|
schema={field}
|
||||||
|
value={values[field.name]}
|
||||||
|
onChange={(v) => onChange(field.name, v)}
|
||||||
|
fieldPath={fieldPath}
|
||||||
|
/>
|
||||||
|
</HookComponent>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无 Hook,使用默认渲染
|
||||||
|
return (
|
||||||
|
<DynamicField
|
||||||
|
schema={field}
|
||||||
|
value={values[field.name]}
|
||||||
|
onChange={(v) => onChange(field.name, v)}
|
||||||
|
fieldPath={fieldPath}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{/* 渲染顶层字段 */}
|
||||||
|
{schema.fields.map((field) => (
|
||||||
|
<div key={field.name}>{renderField(field)}</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* 渲染嵌套 schema */}
|
||||||
|
{schema.nested &&
|
||||||
|
Object.entries(schema.nested).map(([key, nestedSchema]) => (
|
||||||
|
<div key={key} className="mt-6 space-y-4">
|
||||||
|
{/* 嵌套 schema 标题 */}
|
||||||
|
<div className="border-b pb-2">
|
||||||
|
<h3 className="text-lg font-semibold">{nestedSchema.className}</h3>
|
||||||
|
{nestedSchema.classDoc && (
|
||||||
|
<p className="text-sm text-muted-foreground">{nestedSchema.classDoc}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 递归渲染嵌套表单 */}
|
||||||
|
<DynamicConfigForm
|
||||||
|
schema={nestedSchema}
|
||||||
|
values={(values[key] as Record<string, unknown>) || {}}
|
||||||
|
onChange={(field, value) => onChange(`${key}.${field}`, value)}
|
||||||
|
hooks={hooks}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { DynamicConfigForm } from './DynamicConfigForm'
|
||||||
|
export { DynamicField } from './DynamicField'
|
||||||
Loading…
Reference in New Issue