表单设计器

This commit is contained in:
dengqichen 2025-10-23 22:09:14 +08:00
parent 0dabb6aef7
commit b2efb2ffa7
4 changed files with 101 additions and 14 deletions

View File

@ -25,6 +25,7 @@ import {
import { UploadOutlined } from '@ant-design/icons';
import type { FieldConfig } from '../types';
import GridField from './GridField';
import '../styles.css';
const { Text } = Typography;

View File

@ -8,6 +8,7 @@ import { Form, Button, message } from 'antd';
import type { FieldConfig, FormConfig } from '../types';
import FieldRenderer from './FieldRenderer';
import GridFieldPreview from './GridFieldPreview';
import '../styles.css';
interface FormPreviewProps {
fields: FieldConfig[];
@ -96,11 +97,12 @@ const FormPreview: React.FC<FormPreviewProps> = ({ fields, formConfig }) => {
const wrapperColSpan = formConfig.labelAlign === 'left' || formConfig.labelAlign === 'right' ? 18 : undefined;
return (
<div>
<div style={{ padding: '24px 40px' }}>
<Form
form={form}
layout={formLayout}
size={formConfig.size}
colon={true}
labelAlign={formConfig.labelAlign === 'top' ? undefined : formConfig.labelAlign}
labelCol={labelColSpan ? { span: labelColSpan } : undefined}
wrapperCol={wrapperColSpan ? { span: wrapperColSpan } : undefined}
@ -111,13 +113,19 @@ const FormPreview: React.FC<FormPreviewProps> = ({ fields, formConfig }) => {
{renderFields(fields)}
<Form.Item wrapperCol={wrapperColSpan ? { offset: labelColSpan, span: wrapperColSpan } : undefined}>
<Button type="primary" onClick={handleSubmit} style={{ marginRight: 8 }}>
</Button>
<Button onClick={handleReset}>
</Button>
<Form.Item
style={{ marginTop: 32, marginBottom: 0 }}
labelCol={{ span: 0 }}
wrapperCol={{ span: 24 }}
>
<div style={{ display: 'flex', justifyContent: 'center', gap: 8 }}>
<Button type="primary" onClick={handleSubmit}>
</Button>
<Button onClick={handleReset}>
</Button>
</div>
</Form.Item>
</Form>

View File

@ -20,8 +20,15 @@ const GridFieldPreview: React.FC<GridFieldPreviewProps> = ({
onFieldChange
}) => {
const columns = field.columns || 2;
const colSpan = 24 / columns;
const children = field.children || Array(columns).fill([]);
// 使用自定义列宽度或平均分配
const getColSpan = (colIndex: number) => {
if (field.columnSpans && field.columnSpans.length > colIndex) {
return field.columnSpans[colIndex];
}
return 24 / columns; // 平均分配
};
const renderFieldItem = (childField: FieldConfig) => {
// 布局组件直接渲染,不需要 Form.Item
@ -39,6 +46,7 @@ const GridFieldPreview: React.FC<GridFieldPreviewProps> = ({
key={childField.id}
label={childField.label}
name={childField.name}
colon={true}
rules={[
{
required: childField.required,
@ -59,11 +67,14 @@ const GridFieldPreview: React.FC<GridFieldPreviewProps> = ({
return (
<div style={{ marginBottom: 16 }}>
<Row gutter={field.gutter || 16}>
{children.map((columnFields, colIndex) => (
<Col key={colIndex} span={colSpan}>
{columnFields.map((childField: FieldConfig) => renderFieldItem(childField))}
</Col>
))}
{children.map((columnFields, colIndex) => {
const colSpan = getColSpan(colIndex);
return (
<Col key={colIndex} span={colSpan}>
{columnFields.map((childField: FieldConfig) => renderFieldItem(childField))}
</Col>
);
})}
</Row>
</div>
);

View File

@ -133,3 +133,70 @@
opacity: 1;
}
/* 确保所有表单组件高度一致 */
.ant-form-item .ant-input,
.ant-form-item .ant-input-number,
.ant-form-item .ant-input-number-input-wrap,
.ant-form-item .ant-select-selector,
.ant-form-item .ant-picker {
min-height: 32px !important;
height: 32px !important;
}
.ant-form-item .ant-select-single:not(.ant-select-customize-input) .ant-select-selector {
height: 32px !important;
display: flex;
align-items: center;
padding: 0 11px !important;
}
.ant-form-item .ant-select-selection-search-input {
height: 30px !important;
}
.ant-form-item .ant-input-number-input {
height: 30px !important;
}
/* 中等尺寸 */
.ant-form-middle .ant-input,
.ant-form-middle .ant-input-number,
.ant-form-middle .ant-input-number-input-wrap,
.ant-form-middle .ant-select-selector,
.ant-form-middle .ant-picker {
min-height: 32px !important;
height: 32px !important;
}
.ant-form-middle .ant-select-single:not(.ant-select-customize-input) .ant-select-selector {
height: 32px !important;
}
/* 大尺寸 */
.ant-form-large .ant-input,
.ant-form-large .ant-input-number,
.ant-form-large .ant-input-number-input-wrap,
.ant-form-large .ant-select-selector,
.ant-form-large .ant-picker {
min-height: 40px !important;
height: 40px !important;
}
.ant-form-large .ant-select-single:not(.ant-select-customize-input) .ant-select-selector {
height: 40px !important;
}
/* 小尺寸 */
.ant-form-small .ant-input,
.ant-form-small .ant-input-number,
.ant-form-small .ant-input-number-input-wrap,
.ant-form-small .ant-select-selector,
.ant-form-small .ant-picker {
min-height: 24px !important;
height: 24px !important;
}
.ant-form-small .ant-select-single:not(.ant-select-customize-input) .ant-select-selector {
height: 24px !important;
}