表单设计器

This commit is contained in:
dengqichen 2025-10-24 00:06:19 +08:00
parent 8c9813ed55
commit b4836b1c91
6 changed files with 179 additions and 35 deletions

View File

@ -11,7 +11,6 @@ import type { ComponentMeta } from '../config';
import { getComponentsByCategory } from '../config';
import '../styles.css';
const { Panel } = Collapse;
const { Text } = Typography;
// 可拖拽组件项
@ -62,22 +61,26 @@ const ComponentPanel: React.FC = () => {
defaultActiveKey={['基础字段', '高级字段', '布局字段']}
ghost
bordered={false}
>
{categoryOrder.map((category) => {
const components = componentsByCategory[category];
if (!components || components.length === 0) return null;
return (
<Panel header={category} key={category}>
<div className="form-designer-component-list">
{components.map((component) => (
<DraggableComponent key={component.type} component={component} />
))}
</div>
</Panel>
);
})}
</Collapse>
items={categoryOrder
.map((category) => {
const components = componentsByCategory[category];
if (!components || components.length === 0) return null;
return {
key: category,
label: category,
children: (
<div className="form-designer-component-list">
{components.map((component) => (
<DraggableComponent key={component.type} component={component} />
))}
</div>
),
};
})
.filter(Boolean) as any[]
}
/>
</div>
);
};

View File

@ -234,6 +234,12 @@ const FormPreview: React.FC<FormPreviewProps> = ({ fields, formConfig }) => {
// 合并基础验证规则和自定义验证规则
const customRules = convertValidationRules(field.validationRules);
// 🐛 调试:打印验证规则
if (field.validationRules && field.validationRules.length > 0) {
console.log(`📋 字段 "${field.label}" 的验证规则:`, field.validationRules);
console.log(`✅ 转换后的规则:`, customRules);
}
// 检查自定义验证规则中是否已经包含必填验证
const hasRequiredRule = field.validationRules?.some(rule => rule.type === 'required');
@ -247,6 +253,11 @@ const FormPreview: React.FC<FormPreviewProps> = ({ fields, formConfig }) => {
}
const allRules = [...baseRules, ...customRules];
// 🐛 调试:打印最终规则
if (allRules.length > 0) {
console.log(`🎯 字段 "${field.label}" 最终验证规则:`, allRules);
}
// 普通表单组件使用 Form.Item 包裹
return (

View File

@ -5,7 +5,8 @@
import React from 'react';
import { Row, Col, Form } from 'antd';
import type { FieldConfig, FormConfig } from '../types';
import type { Rule } from 'antd/es/form';
import type { FieldConfig, FormConfig, ValidationRule } from '../types';
import FieldRenderer from './FieldRenderer';
interface GridFieldPreviewProps {
@ -32,6 +33,51 @@ const GridFieldPreview: React.FC<GridFieldPreviewProps> = ({
return 24 / columns; // 平均分配
};
// 将验证规则转换为Ant Design的Rule数组和FormPreview保持一致
const convertValidationRules = (validationRules?: ValidationRule[]): Rule[] => {
if (!validationRules || validationRules.length === 0) return [];
return validationRules.map(rule => {
const antdRule: Rule = {
type: rule.type === 'email' ? 'email' : rule.type === 'url' ? 'url' : undefined,
message: rule.message || `请输入正确的${rule.type}`,
};
switch (rule.type) {
case 'required':
antdRule.required = true;
break;
case 'pattern':
if (rule.value) {
antdRule.pattern = new RegExp(rule.value);
}
break;
case 'min':
antdRule.type = 'number';
antdRule.min = rule.value;
break;
case 'max':
antdRule.type = 'number';
antdRule.max = rule.value;
break;
case 'minLength':
antdRule.min = rule.value;
break;
case 'maxLength':
antdRule.max = rule.value;
break;
case 'phone':
antdRule.pattern = /^1[3-9]\d{9}$/;
break;
case 'idCard':
antdRule.pattern = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dX]$/;
break;
}
return antdRule;
});
};
const renderFieldItem = (childField: FieldConfig) => {
// 布局组件直接渲染,不需要 Form.Item
if (['text', 'divider', 'grid'].includes(childField.type)) {
@ -45,6 +91,34 @@ const GridFieldPreview: React.FC<GridFieldPreviewProps> = ({
// 根据表单配置决定布局方式
const isVertical = formConfig?.labelAlign === 'top';
// 合并基础验证规则和自定义验证规则和FormPreview保持一致
const customRules = convertValidationRules(childField.validationRules);
// 🐛 调试:打印验证规则
if (childField.validationRules && childField.validationRules.length > 0) {
console.log(`📋 [GridFieldPreview] 字段 "${childField.label}" 的验证规则:`, childField.validationRules);
console.log(`✅ [GridFieldPreview] 转换后的规则:`, customRules);
}
// 检查自定义验证规则中是否已经包含必填验证
const hasRequiredRule = childField.validationRules?.some(rule => rule.type === 'required');
// 基础规则:只有在没有自定义必填验证时,才使用字段属性中的"是否必填"
const baseRules: Rule[] = [];
if (childField.required && !hasRequiredRule) {
baseRules.push({
required: true,
message: `请输入${childField.label}`,
});
}
const allRules = [...baseRules, ...customRules];
// 🐛 调试:打印最终规则
if (allRules.length > 0) {
console.log(`🎯 [GridFieldPreview] 字段 "${childField.label}" 最终验证规则:`, allRules);
}
// 普通表单字段用 Form.Item 包裹
// 栅格内字段:根据对齐方式使用不同的布局
return (
@ -55,12 +129,7 @@ const GridFieldPreview: React.FC<GridFieldPreviewProps> = ({
colon={true}
labelCol={isVertical ? { span: 24 } : { span: 8 }}
wrapperCol={isVertical ? { span: 24 } : { span: 16 }}
rules={[
{
required: childField.required,
message: `请输入${childField.label}`,
},
]}
rules={allRules}
>
<FieldRenderer
field={childField}

View File

@ -25,7 +25,6 @@ import ValidationRuleEditor from './ValidationRuleEditor';
import LinkageRuleEditor from './LinkageRuleEditor';
const { Text } = Typography;
const { TabPane } = Tabs;
interface PropertyPanelProps {
selectedField: FieldConfig | null;
@ -62,6 +61,8 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
predefinedCascadeDataSource: selectedField.predefinedCascadeDataSource || {
sourceType: '',
},
validationRules: selectedField.validationRules || [],
linkageRules: selectedField.linkageRules || [],
};
form.setFieldsValue(formValues);
@ -132,9 +133,18 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
key !== 'predefinedDataSource' &&
key !== 'predefinedCascadeDataSource') {
(updatedField as any)[key] = changedValues[key];
// 🐛 调试:打印验证规则和联动规则的更新
if (key === 'validationRules') {
console.log('💾 [PropertyPanel] 验证规则已更新:', changedValues[key]);
}
if (key === 'linkageRules') {
console.log('💾 [PropertyPanel] 联动规则已更新:', changedValues[key]);
}
}
});
console.log('🔄 [PropertyPanel] 字段配置更新完成:', updatedField);
onFieldChange(updatedField);
}
};
@ -659,14 +669,22 @@ const PropertyPanel: React.FC<PropertyPanelProps> = ({
<Text strong></Text>
</div>
<Tabs defaultActiveKey="field" style={{ padding: '0 0 0 16px' }}>
<TabPane tab="字段属性" key="field">
{renderFieldProperties()}
</TabPane>
<TabPane tab="表单属性" key="form">
{renderFormProperties()}
</TabPane>
</Tabs>
<Tabs
defaultActiveKey="field"
style={{ padding: '0 0 0 16px' }}
items={[
{
key: 'field',
label: '字段属性',
children: renderFieldProperties(),
},
{
key: 'form',
label: '表单属性',
children: renderFormProperties(),
},
]}
/>
</div>
);
};

View File

@ -21,17 +21,21 @@ const ValidationRuleEditor: React.FC<ValidationRuleEditorProps> = ({ value = [],
message: '',
trigger: 'blur',
};
onChange?.([...value, newRule]);
const newRules = [...value, newRule];
console.log(' [ValidationRuleEditor] 添加验证规则:', newRules);
onChange?.(newRules);
};
const handleDeleteRule = (index: number) => {
const newRules = value.filter((_, i) => i !== index);
console.log('🗑️ [ValidationRuleEditor] 删除验证规则:', newRules);
onChange?.(newRules);
};
const handleRuleChange = (index: number, field: keyof ValidationRule, fieldValue: any) => {
const newRules = [...value];
newRules[index] = { ...newRules[index], [field]: fieldValue };
console.log(`✏️ [ValidationRuleEditor] 修改验证规则 [${field}]:`, newRules);
onChange?.(newRules);
};

View File

@ -248,6 +248,12 @@ const FormDesigner: React.FC = () => {
// 更新字段属性(支持更新栅格内的字段)
const handleFieldChange = useCallback((updatedField: FieldConfig) => {
// 🐛 调试:打印字段更新
console.log('🔄 [FormDesigner] 字段更新:', updatedField);
if (updatedField.validationRules && updatedField.validationRules.length > 0) {
console.log('✅ [FormDesigner] 字段包含验证规则:', updatedField.validationRules);
}
// 递归更新函数
const updateFieldRecursive = (fieldList: FieldConfig[]): FieldConfig[] => {
return fieldList.map(f => {
@ -267,7 +273,11 @@ const FormDesigner: React.FC = () => {
});
};
setFields(prev => updateFieldRecursive(prev));
setFields(prev => {
const newFields = updateFieldRecursive(prev);
console.log('💾 [FormDesigner] 字段列表已更新');
return newFields;
});
}, []);
// 更新表单配置
@ -527,6 +537,35 @@ const FormDesigner: React.FC = () => {
onCancel={() => setPreviewVisible(false)}
width={formConfig.formWidth || 600}
footer={null}
afterOpenChange={(open) => {
if (open) {
console.log('🎬 [Modal] 打开预览,当前字段列表:', fields);
// 递归检查所有字段包括grid内的嵌套字段
const checkFieldsRecursive = (fieldList: FieldConfig[], level = 0) => {
fieldList.forEach(field => {
const indent = ' '.repeat(level);
console.log(`${indent}📦 [Modal] 字段: "${field.label}" (${field.type})`, field);
if (field.validationRules && field.validationRules.length > 0) {
console.log(`${indent}📋 [Modal] ✅ 包含验证规则:`, field.validationRules);
} else {
console.log(`${indent}📋 [Modal] ❌ 没有验证规则`);
}
// 递归检查grid的子字段
if (field.type === 'grid' && field.children) {
field.children.forEach((columnFields, colIndex) => {
console.log(`${indent} 🔸 Grid列 ${colIndex + 1}:`);
checkFieldsRecursive(columnFields, level + 2);
});
}
});
};
checkFieldsRecursive(fields);
}
}}
>
<FormPreview fields={fields} formConfig={formConfig} />
</Modal>