表单设计器
This commit is contained in:
parent
0dabb6aef7
commit
b2efb2ffa7
@ -25,6 +25,7 @@ import {
|
|||||||
import { UploadOutlined } from '@ant-design/icons';
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
import type { FieldConfig } from '../types';
|
import type { FieldConfig } from '../types';
|
||||||
import GridField from './GridField';
|
import GridField from './GridField';
|
||||||
|
import '../styles.css';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { Form, Button, message } from 'antd';
|
|||||||
import type { FieldConfig, FormConfig } from '../types';
|
import type { FieldConfig, FormConfig } from '../types';
|
||||||
import FieldRenderer from './FieldRenderer';
|
import FieldRenderer from './FieldRenderer';
|
||||||
import GridFieldPreview from './GridFieldPreview';
|
import GridFieldPreview from './GridFieldPreview';
|
||||||
|
import '../styles.css';
|
||||||
|
|
||||||
interface FormPreviewProps {
|
interface FormPreviewProps {
|
||||||
fields: FieldConfig[];
|
fields: FieldConfig[];
|
||||||
@ -96,11 +97,12 @@ const FormPreview: React.FC<FormPreviewProps> = ({ fields, formConfig }) => {
|
|||||||
const wrapperColSpan = formConfig.labelAlign === 'left' || formConfig.labelAlign === 'right' ? 18 : undefined;
|
const wrapperColSpan = formConfig.labelAlign === 'left' || formConfig.labelAlign === 'right' ? 18 : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style={{ padding: '24px 40px' }}>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout={formLayout}
|
layout={formLayout}
|
||||||
size={formConfig.size}
|
size={formConfig.size}
|
||||||
|
colon={true}
|
||||||
labelAlign={formConfig.labelAlign === 'top' ? undefined : formConfig.labelAlign}
|
labelAlign={formConfig.labelAlign === 'top' ? undefined : formConfig.labelAlign}
|
||||||
labelCol={labelColSpan ? { span: labelColSpan } : undefined}
|
labelCol={labelColSpan ? { span: labelColSpan } : undefined}
|
||||||
wrapperCol={wrapperColSpan ? { span: wrapperColSpan } : undefined}
|
wrapperCol={wrapperColSpan ? { span: wrapperColSpan } : undefined}
|
||||||
@ -111,13 +113,19 @@ const FormPreview: React.FC<FormPreviewProps> = ({ fields, formConfig }) => {
|
|||||||
|
|
||||||
{renderFields(fields)}
|
{renderFields(fields)}
|
||||||
|
|
||||||
<Form.Item wrapperCol={wrapperColSpan ? { offset: labelColSpan, span: wrapperColSpan } : undefined}>
|
<Form.Item
|
||||||
<Button type="primary" onClick={handleSubmit} style={{ marginRight: 8 }}>
|
style={{ marginTop: 32, marginBottom: 0 }}
|
||||||
提交
|
labelCol={{ span: 0 }}
|
||||||
</Button>
|
wrapperCol={{ span: 24 }}
|
||||||
<Button onClick={handleReset}>
|
>
|
||||||
重置
|
<div style={{ display: 'flex', justifyContent: 'center', gap: 8 }}>
|
||||||
</Button>
|
<Button type="primary" onClick={handleSubmit}>
|
||||||
|
提交
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleReset}>
|
||||||
|
重置
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
|
|||||||
@ -20,8 +20,15 @@ const GridFieldPreview: React.FC<GridFieldPreviewProps> = ({
|
|||||||
onFieldChange
|
onFieldChange
|
||||||
}) => {
|
}) => {
|
||||||
const columns = field.columns || 2;
|
const columns = field.columns || 2;
|
||||||
const colSpan = 24 / columns;
|
|
||||||
const children = field.children || Array(columns).fill([]);
|
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) => {
|
const renderFieldItem = (childField: FieldConfig) => {
|
||||||
// 布局组件直接渲染,不需要 Form.Item
|
// 布局组件直接渲染,不需要 Form.Item
|
||||||
@ -39,6 +46,7 @@ const GridFieldPreview: React.FC<GridFieldPreviewProps> = ({
|
|||||||
key={childField.id}
|
key={childField.id}
|
||||||
label={childField.label}
|
label={childField.label}
|
||||||
name={childField.name}
|
name={childField.name}
|
||||||
|
colon={true}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: childField.required,
|
required: childField.required,
|
||||||
@ -59,11 +67,14 @@ const GridFieldPreview: React.FC<GridFieldPreviewProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div style={{ marginBottom: 16 }}>
|
<div style={{ marginBottom: 16 }}>
|
||||||
<Row gutter={field.gutter || 16}>
|
<Row gutter={field.gutter || 16}>
|
||||||
{children.map((columnFields, colIndex) => (
|
{children.map((columnFields, colIndex) => {
|
||||||
<Col key={colIndex} span={colSpan}>
|
const colSpan = getColSpan(colIndex);
|
||||||
{columnFields.map((childField: FieldConfig) => renderFieldItem(childField))}
|
return (
|
||||||
</Col>
|
<Col key={colIndex} span={colSpan}>
|
||||||
))}
|
{columnFields.map((childField: FieldConfig) => renderFieldItem(childField))}
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -133,3 +133,70 @@
|
|||||||
opacity: 1;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user