表单设计器
This commit is contained in:
parent
8c9813ed55
commit
b4836b1c91
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user