From 3e40119f0962c5ab1d6c2cae4146b9e24fde89d9 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Mon, 3 Nov 2025 22:38:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BB=A3=E7=A0=81=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8=E8=A1=A8=E5=8D=95=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/FormDesigner/Preview.tsx | 13 +- .../src/components/FormDesigner/Renderer.tsx | 18 +- .../FormDesigner/components/FieldRenderer.tsx | 236 +++--------------- .../components/FormFieldsRenderer.tsx | 45 +++- .../FormDesigner/components/GridField.tsx | 11 +- .../components/GridFieldPreview.tsx | 116 +++------ .../FormDesigner/components/PropertyPanel.tsx | 8 + .../FormDesigner/hooks/useFormCore.ts | 33 ++- .../renderers/FieldRenderStrategyRegistry.ts | 71 ++++++ .../renderers/IFieldRenderStrategy.ts | 34 +++ .../strategies/BaseFieldStrategy.tsx | 31 +++ .../renderers/strategies/CascaderStrategy.tsx | 31 +++ .../renderers/strategies/CheckboxStrategy.tsx | 32 +++ .../renderers/strategies/DateStrategy.tsx | 28 +++ .../renderers/strategies/DateTimeStrategy.tsx | 29 +++ .../renderers/strategies/InputStrategy.tsx | 28 +++ .../renderers/strategies/NumberStrategy.tsx | 30 +++ .../renderers/strategies/RadioStrategy.tsx | 27 ++ .../renderers/strategies/RateStrategy.tsx | 26 ++ .../renderers/strategies/SelectStrategy.tsx | 29 +++ .../renderers/strategies/SliderStrategy.tsx | 28 +++ .../renderers/strategies/SwitchStrategy.tsx | 26 ++ .../renderers/strategies/TextAreaStrategy.tsx | 31 +++ .../renderers/strategies/TimeStrategy.tsx | 28 +++ .../renderers/strategies/UploadStrategy.tsx | 28 +++ .../src/components/FormDesigner/styles.css | 21 ++ frontend/src/components/FormDesigner/types.ts | 2 + .../FormDesigner/utils/defaultValueHelper.ts | 41 --- .../FormDesigner/utils/fieldStateHelper.ts | 73 ++++++ .../FormDesigner/utils/formLayoutHelper.ts | 60 +++++ .../dataSource/CascadeDataSourceRegistry.ts | 54 ++-- .../dataSource/CascadeDataSourceService.ts | 145 ++++++----- .../domain/dataSource/DataSourceRegistry.ts | 89 ++++--- .../domain/dataSource/DataSourceService.ts | 16 +- .../domain/dataSource/core/BaseRegistry.ts | 47 ++++ .../domain/dataSource/core/errorHandler.ts | 37 +++ .../domain/dataSource/core/requestHelper.ts | 33 +++ .../src/domain/dataSource/presets/deploy.ts | 55 ---- .../src/domain/dataSource/presets/docker.ts | 17 -- frontend/src/domain/dataSource/presets/git.ts | 17 -- .../dataSource/presets/infrastructure.ts | 190 ++++++++++++++ .../src/domain/dataSource/presets/jenkins.ts | 18 -- frontend/src/domain/dataSource/presets/k8s.ts | 17 -- .../domain/dataSource/presets/notification.ts | 25 -- .../src/domain/dataSource/presets/user.ts | 43 ---- 45 files changed, 1332 insertions(+), 685 deletions(-) create mode 100644 frontend/src/components/FormDesigner/renderers/FieldRenderStrategyRegistry.ts create mode 100644 frontend/src/components/FormDesigner/renderers/IFieldRenderStrategy.ts create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/BaseFieldStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/CascaderStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/CheckboxStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/DateStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/DateTimeStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/InputStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/NumberStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/RadioStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/RateStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/SelectStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/SliderStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/SwitchStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/TextAreaStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/TimeStrategy.tsx create mode 100644 frontend/src/components/FormDesigner/renderers/strategies/UploadStrategy.tsx delete mode 100644 frontend/src/components/FormDesigner/utils/defaultValueHelper.ts create mode 100644 frontend/src/components/FormDesigner/utils/fieldStateHelper.ts create mode 100644 frontend/src/components/FormDesigner/utils/formLayoutHelper.ts create mode 100644 frontend/src/domain/dataSource/core/BaseRegistry.ts create mode 100644 frontend/src/domain/dataSource/core/errorHandler.ts create mode 100644 frontend/src/domain/dataSource/core/requestHelper.ts delete mode 100644 frontend/src/domain/dataSource/presets/deploy.ts delete mode 100644 frontend/src/domain/dataSource/presets/docker.ts delete mode 100644 frontend/src/domain/dataSource/presets/git.ts create mode 100644 frontend/src/domain/dataSource/presets/infrastructure.ts delete mode 100644 frontend/src/domain/dataSource/presets/jenkins.ts delete mode 100644 frontend/src/domain/dataSource/presets/k8s.ts delete mode 100644 frontend/src/domain/dataSource/presets/notification.ts delete mode 100644 frontend/src/domain/dataSource/presets/user.ts diff --git a/frontend/src/components/FormDesigner/Preview.tsx b/frontend/src/components/FormDesigner/Preview.tsx index 8db383e9..03575272 100644 --- a/frontend/src/components/FormDesigner/Preview.tsx +++ b/frontend/src/components/FormDesigner/Preview.tsx @@ -35,6 +35,7 @@ import { useImperativeHandle, forwardRef } from 'react'; import { Form, message } from 'antd'; import type { FieldConfig, FormConfig } from './types'; import { useFormCore } from './hooks/useFormCore'; +import { computeFormLayout } from './utils/formLayoutHelper'; import FormFieldsRenderer from './components/FormFieldsRenderer'; import './styles.css'; @@ -96,20 +97,18 @@ const FormPreview = forwardRef(({ fields, form ); } - const formLayout = formConfig.labelAlign === 'top' ? 'vertical' : 'horizontal'; - const labelColSpan = formConfig.labelAlign === 'left' ? 6 : formConfig.labelAlign === 'right' ? 6 : undefined; - const wrapperColSpan = formConfig.labelAlign === 'left' || formConfig.labelAlign === 'right' ? 18 : undefined; + const layoutConfig = computeFormLayout(formConfig); return (
{formConfig.title && ( diff --git a/frontend/src/components/FormDesigner/Renderer.tsx b/frontend/src/components/FormDesigner/Renderer.tsx index 1ca8b9a3..9974af74 100644 --- a/frontend/src/components/FormDesigner/Renderer.tsx +++ b/frontend/src/components/FormDesigner/Renderer.tsx @@ -50,6 +50,7 @@ import type { ComponentMeta } from './config'; import { COMPONENT_LIST } from './config'; import { ComponentsContext } from './Designer'; import { useFormCore } from './hooks/useFormCore'; +import { computeFormLayout } from './utils/formLayoutHelper'; import FormFieldsRenderer from './components/FormFieldsRenderer'; import './styles.css'; @@ -230,21 +231,19 @@ const FormRenderer = forwardRef((props, ref) ); } - const formLayout = formConfig.labelAlign === 'top' ? 'vertical' : 'horizontal'; - const labelColSpan = formConfig.labelAlign === 'left' ? 6 : formConfig.labelAlign === 'right' ? 6 : undefined; - const wrapperColSpan = formConfig.labelAlign === 'left' || formConfig.labelAlign === 'right' ? 18 : undefined; + const layoutConfig = computeFormLayout(formConfig); return (
{formConfig.title && ( @@ -260,7 +259,10 @@ const FormRenderer = forwardRef((props, ref) {(showSubmit || showCancel) && (
{showSubmit && ( diff --git a/frontend/src/components/FormDesigner/components/FieldRenderer.tsx b/frontend/src/components/FormDesigner/components/FieldRenderer.tsx index 96f2b65d..f8e3887d 100644 --- a/frontend/src/components/FormDesigner/components/FieldRenderer.tsx +++ b/frontend/src/components/FormDesigner/components/FieldRenderer.tsx @@ -8,32 +8,19 @@ import { ComponentsContext } from '../Designer'; import { Form, Input, - InputNumber, - Select, - Radio, - Checkbox, - DatePicker, - TimePicker, - Switch, - Slider, - Rate, - Upload, - Cascader, Divider, - Button, Typography, } from 'antd'; -import { UploadOutlined } from '@ant-design/icons'; import type { FieldConfig } from '../types'; import GridField from './GridField'; import { useFieldOptions } from '../hooks/useFieldOptions'; import { useCascaderOptions, useCascaderLoadData } from '../hooks/useCascaderOptions'; +import { fieldRenderStrategyRegistry } from '../renderers/FieldRenderStrategyRegistry'; +import type { FieldRenderContext } from '../renderers/IFieldRenderStrategy'; import '../styles.css'; const { Text } = Typography; -const { TextArea } = Input; - interface FieldRendererProps { field: FieldConfig; value?: any; @@ -63,40 +50,27 @@ const FieldRenderer: React.FC = ({ hasClipboard = false, duplicateFieldIds = new Set(), }) => { - // 获取字段选项(支持静态和动态数据源) + // 获取字段选项和级联选项(支持静态和动态数据源) const options = useFieldOptions(field); - - // 获取级联选择器选项和懒加载函数 const cascadeOptions = useCascaderOptions(field); const loadData = useCascaderLoadData(field); - // 🔌 检查是否有自定义渲染组件(插件式扩展) + // 🔌 优先检查自定义渲染组件(插件式扩展) const allComponents = useContext(ComponentsContext); const componentMeta = allComponents.find(c => c.type === field.type); if (componentMeta?.FieldRendererComponent) { const CustomRenderer = componentMeta.FieldRendererComponent; - console.log('✅ FieldRenderer: 使用自定义渲染组件', { - fieldType: field.type, - isPreview, - hasRenderer: !!componentMeta.FieldRendererComponent - }); return ; } - // 布局组件特殊处理 + // 布局组件特殊处理(这些组件不通过策略模式) if (field.type === 'divider') { - const showText = field.showText !== false; // 默认显示文字 + const showText = field.showText !== false; const dividerText = field.dividerText || field.label || '分割线'; const dividerColor = field.dividerColor || 'rgba(5, 5, 5, 0.06)'; - return ( - + {showText ? dividerText : null} ); @@ -104,10 +78,7 @@ const FieldRenderer: React.FC = ({ if (field.type === 'text') { return ( -
+
= ({ ); } + // 使用策略模式渲染字段(工厂模式获取策略) + const strategy = fieldRenderStrategyRegistry.get(field.type); + + // 计算只读样式 + const isReadonly = field.readonly || false; + const readonlyStyle = isReadonly ? { + backgroundColor: '#f5f5f5', + cursor: 'not-allowed', + color: '#00000040', + } : undefined; + + // 构造渲染上下文 + const renderContext: FieldRenderContext = { + field, + value, + onChange, + isDisabled: field.disabled || false, + isReadonly, + readonlyStyle, + options, + cascadeOptions, + loadData, + }; + + // 渲染字段组件 const renderField = () => { - switch (field.type) { - case 'input': + if (strategy) { + return strategy.render(renderContext); + } + + // 兜底策略:未注册的字段类型使用默认 Input return ( onChange?.(e.target.value)} + readOnly={isReadonly} + disabled={field.disabled && !isReadonly} /> ); - - case 'textarea': - return ( -