= () => <>Demo!>;
diff --git a/docs/components/form/form-item.md b/docs/components/form/form-item.md
index bbb1440..df2c355 100644
--- a/docs/components/form/form-item.md
+++ b/docs/components/form/form-item.md
@@ -37,6 +37,10 @@ const SuperInput = (props) => {
+## itemSpan 占用栅格数
+
+
+
## 隐藏 label
diff --git a/docs/components/form/form.md b/docs/components/form/form.md
index f102ebd..9dd83e2 100644
--- a/docs/components/form/form.md
+++ b/docs/components/form/form.md
@@ -99,6 +99,12 @@ antd Form 当 `Form.Item` 无 `label` 时,还需要指定其 labelCol 的 `off
+## 表单项布局
+
+我们可以通过 `itemCount` 设置一列显示多少表单项,并且表单项可以通过 `itemSpan` 自定义显示的占用的栅格数。
+
+
+
## debug 功能
SuperForm 增加 `debug` 功能,通过 `debug` 属性即可开启。
diff --git a/src/form-item/src/hoc/withFormItem.tsx b/src/form-item/src/hoc/withFormItem.tsx
index 57c961d..7528389 100644
--- a/src/form-item/src/hoc/withFormItem.tsx
+++ b/src/form-item/src/hoc/withFormItem.tsx
@@ -1,5 +1,6 @@
import { compilerStr } from '@dream2023/data-mapping';
import { useCreation } from 'ahooks';
+import { Col } from 'antd';
import set from 'lodash.set';
import type { ComponentType, FC } from 'react';
import React, { useContext, useEffect } from 'react';
@@ -51,6 +52,7 @@ export function withFormItem(
disabledOn,
readonlyOn,
requiredOn,
+ itemSpan,
wrapperCol,
placeholder,
validateStatus,
@@ -64,7 +66,7 @@ export function withFormItem
(
const { delimiters } = useContext(SuperAntdContext);
// 表单 context
const formContext = useContext(SuperFormContext);
- const { form, initialValues } = formContext;
+ const { form, initialValues, itemCount: formItemCount } = formContext;
const data = Object.assign({}, initialValues, form?.getFieldsValue());
const { layout, autoPlaceholder, hideLabel: formHideLabel, remoteErrors } = formContext;
@@ -162,6 +164,13 @@ export function withFormItem(
return needData ? { data, form } : {};
}, [data]);
+ // 合并 formItemCount 和 本身 itemSpan
+ const computedItemLayout = useCreation(() => {
+ if (itemSpan) return itemSpan;
+ if (formItemCount) return 24 / formItemCount;
+ return 24;
+ }, [itemSpan, formItemCount]);
+
// 动态必填,参考:https://ant.design/components/form-cn/#components-form-demo-dynamic-rule
useEffect(() => {
if (computedName) {
@@ -207,7 +216,7 @@ export function withFormItem
(
return { help, validateStatus };
}, [help, validateStatus, remoteErrors, computedName]);
- return (
+ const FormItemContent = (
(
messageVariables={computedMessageVariables}
/>
);
+
+ return computedItemLayout !== 24 ? {FormItemContent} : FormItemContent;
};
return EnhancedFormComponent;
diff --git a/src/form-item/src/hoc/withFormItemTypes.ts b/src/form-item/src/hoc/withFormItemTypes.ts
index bde408c..7ce76a1 100644
--- a/src/form-item/src/hoc/withFormItemTypes.ts
+++ b/src/form-item/src/hoc/withFormItemTypes.ts
@@ -40,6 +40,8 @@ export interface NewWithFormItemProps {
clearValueAfterReadonly?: boolean;
/** 是否隐藏 label。当我们想要保留 label 作为校检的名称,又不想显示 label 时,可以将其设置为 true。 */
hideLabel?: boolean;
+ /** 表单项所占栅格数量 */
+ itemSpan?: number;
}
// 更改原表单项属性
@@ -76,6 +78,7 @@ export const omitWithFormItemKeys: Record = {
readonlyOn: '',
wrapperCol: '',
requiredOn: '',
+ itemSpan: '',
linkageFields: '',
clearValueAfterHidden: '',
clearValueAfterDisabled: '',
diff --git a/src/form/src/Form.tsx b/src/form/src/Form.tsx
index b2685a9..b9410ec 100644
--- a/src/form/src/Form.tsx
+++ b/src/form/src/Form.tsx
@@ -3,7 +3,7 @@ import './form.less';
import ProForm from '@ant-design/pro-form';
import type { ProFormProps } from '@ant-design/pro-form';
import { useCreation, useLocalStorageState, usePersistFn, useThrottleFn } from 'ahooks';
-import { Form, Spin } from 'antd';
+import { Form, Row, Spin } from 'antd';
import type { Key } from 'react';
import { useContext } from 'react';
import React, { useState } from 'react';
@@ -60,6 +60,7 @@ export function SuperForm = any>(props: SuperFor
// 响应式和标签
labelCol,
+ itemCount,
wrapperCol,
isResponsive,
@@ -236,12 +237,13 @@ export function SuperForm = any>(props: SuperFor
readonly,
disabled,
hideLabel,
+ itemCount,
form: formInstance,
autoPlaceholder,
remoteErrors,
initialValues: initialValuesWithStorage,
};
- }, [remoteErrors, layout, formInstance, readonly, disabled, hideLabel, autoPlaceholder]);
+ }, [remoteErrors, itemCount, layout, formInstance, readonly, disabled, hideLabel, autoPlaceholder]);
// loading 效果(初始化和提交数据时)
const loading = useCreation(() => {
@@ -251,6 +253,7 @@ export function SuperForm = any>(props: SuperFor
// 响应式
const { responsiveRef, responsiveLabelCol, responsiveWrapperCol } = useResponsiveCol({
layout,
+ itemCount,
align,
labelCol,
hideLabel,
@@ -331,7 +334,9 @@ export function SuperForm = any>(props: SuperFor
{debug && }
{/* children 数据 */}
- {children}
+
+ {Number(itemCount) > 1 ? {children}
: children}
+
{btnDoms && (
= any>(props: SuperFor
}
SuperForm.defaultProps = {
+ itemCount: 1,
autoPlaceholder: true,
clearPersistDataAfterSubmit: true,
btns: {
diff --git a/src/form/src/context.tsx b/src/form/src/context.tsx
index 2b3bb6a..c54f6bb 100644
--- a/src/form/src/context.tsx
+++ b/src/form/src/context.tsx
@@ -10,6 +10,7 @@ export interface SuperFormContextProps = any> ex
layout?: 'horizontal' | 'vertical' | 'inline';
/** 服务端返回的错误 */
remoteErrors: Record;
+ itemCount?: number;
initialValues?: Record;
}
diff --git a/src/form/src/types.ts b/src/form/src/types.ts
index 6dc1bb8..e63d1bf 100644
--- a/src/form/src/types.ts
+++ b/src/form/src/types.ts
@@ -148,4 +148,8 @@ export interface FormEnhancedProps {
align?: 'left' | 'right' | 'center';
/** 是否保留远程获取的非表单项里的数据,例如 {id: 1, name: 'Jerry'} id 未设置表单项,antd 默认会删除,如果开启,则保留 */
preserveRemoteData?: boolean;
+ /**
+ * 每行所占的表单项个数
+ */
+ itemCount?: number;
}
diff --git a/src/shared/src/hooks/useResponsiveCol/index.ts b/src/shared/src/hooks/useResponsiveCol/index.ts
index 202d967..36df128 100644
--- a/src/shared/src/hooks/useResponsiveCol/index.ts
+++ b/src/shared/src/hooks/useResponsiveCol/index.ts
@@ -45,6 +45,8 @@ export interface ResponsiveColOptions {
layout?: FormLayout;
// 是否隐藏标签
hideLabel?: boolean;
+ // 表单项布局
+ itemCount?: number;
// 是否开启响应式
isResponsive?: boolean;
// 对齐方式
@@ -54,6 +56,7 @@ export interface ResponsiveColOptions {
export const useResponsiveCol = ({
layout = 'horizontal',
+ itemCount = 1,
isResponsive = true,
labelCol,
align = 'left',
@@ -91,9 +94,11 @@ export const useResponsiveCol = ({
// 判断是否已经开启响应式
const shouldResponsive = useCreation(() => {
- // isResponsive 为 true 并且没有指定 labelCol 和 wrapperCol
- return layout === 'horizontal' && align === 'left' && isResponsive && !labelCol && !wrapperCol;
- }, [layout, isResponsive, labelCol, wrapperCol, align]);
+ // itemCount < 1 且 isResponsive 为 true 并且没有指定 labelCol 和 wrapperCol
+ return (
+ Number(itemCount) <= 1 && layout === 'horizontal' && align === 'left' && isResponsive && !labelCol && !wrapperCol
+ );
+ }, [layout, itemCount, isResponsive, labelCol, wrapperCol, align]);
// 通过 ref 绑定 DOM
const responsiveRef = useCreation(() => {
diff --git a/tests/form/Form.test.tsx b/tests/form/Form.test.tsx
index a5916fa..d20ec51 100644
--- a/tests/form/Form.test.tsx
+++ b/tests/form/Form.test.tsx
@@ -652,4 +652,48 @@ describe('SuperForm 表单', () => {
expect(wrapper.container.querySelector('super-antd-left')).not.toBeInTheDocument();
});
});
+
+ describe('itemCount 表单项个数', () => {
+ test('当未设置 itemCount 或者 itemCount = 1 时,不起作用', () => {
+ // 第一次渲染
+ const { container, rerender } = render();
+ const count = container.querySelectorAll('.ant-form > .ant-form-item').length;
+
+ // 第二次渲染,查看子元素数量
+ rerender(
+
+
+ ,
+ );
+ const count2 = container.querySelectorAll('.ant-form > .ant-form-item').length;
+ expect(count + 1).toBe(count2);
+
+ // 第三次渲染,当设置了 1,查看子元素数量
+ rerender(
+
+
+ ,
+ );
+ const count3 = container.querySelectorAll('.ant-form > .ant-form-item').length;
+ expect(count2).toBe(count3);
+ });
+
+ test('当设置 itemCount 为 3 时,表单项栅格数应为 8', () => {
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.querySelector('.ant-row > .ant-col-8')).toBeInTheDocument();
+ });
+
+ test('当表单项设置 itemSpan 为值时,起到覆盖作用', () => {
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.querySelector('.ant-row > .ant-col-16')).toBeInTheDocument();
+ });
+ });
});
diff --git a/tests/shared/hooks/useResponsiveCol.test.tsx b/tests/shared/hooks/useResponsiveCol.test.tsx
index 43db378..2ae61b2 100644
--- a/tests/shared/hooks/useResponsiveCol.test.tsx
+++ b/tests/shared/hooks/useResponsiveCol.test.tsx
@@ -15,6 +15,12 @@ describe('useResponsiveCol', () => {
const { result } = setup({ isResponsive: false });
expect(result.current.shouldResponsive).toBeFalsy();
});
+
+ test('itemCount > 1,则为 false', () => {
+ const { result } = setup({ itemCount: 2 });
+ expect(result.current.shouldResponsive).toBeFalsy();
+ });
+
test('labelCol 为存在,则为 false', () => {
const { result } = setup({ labelCol: 9 });
expect(result.current.shouldResponsive).toBeFalsy();
diff --git a/yarn.lock b/yarn.lock
index 65de35a..a7f31b6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5556,8 +5556,8 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000971, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001166, caniuse-lite@^1.0.30001179, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001219:
- version "1.0.30001241"
- resolved "https://registry.npm.taobao.org/caniuse-lite/download/caniuse-lite-1.0.30001241.tgz"
+ version "1.0.30001243"
+ resolved "https://registry.nlark.com/caniuse-lite/download/caniuse-lite-1.0.30001243.tgz"
capture-exit@^2.0.0:
version "2.0.0"