From c92ab974201dd4c3fb74ad4c497c697b25fc9109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E6=9D=B0?= <1098626505@qq.com> Date: Sun, 11 Jul 2021 15:50:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(#13):=20=E5=A2=9E=E5=8A=A0=20itemCount=20?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E7=9A=84=E5=B8=83=E5=B1=80=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=20(#17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../form/__demos__/form-item/itemSpan.tsx | 17 +++++++ .../form/__demos__/form-item/types.tsx | 2 + .../form/__demos__/form/itemCount.tsx | 35 +++++++++++++++ docs/components/form/__demos__/form/types.tsx | 2 + docs/components/form/form-item.md | 4 ++ docs/components/form/form.md | 6 +++ src/form-item/src/hoc/withFormItem.tsx | 15 ++++++- src/form-item/src/hoc/withFormItemTypes.ts | 3 ++ src/form/src/Form.tsx | 12 +++-- src/form/src/context.tsx | 1 + src/form/src/types.ts | 4 ++ .../src/hooks/useResponsiveCol/index.ts | 11 +++-- tests/form/Form.test.tsx | 44 +++++++++++++++++++ tests/shared/hooks/useResponsiveCol.test.tsx | 6 +++ yarn.lock | 4 +- 15 files changed, 156 insertions(+), 10 deletions(-) create mode 100644 docs/components/form/__demos__/form-item/itemSpan.tsx create mode 100644 docs/components/form/__demos__/form/itemCount.tsx diff --git a/docs/components/form/__demos__/form-item/itemSpan.tsx b/docs/components/form/__demos__/form-item/itemSpan.tsx new file mode 100644 index 0000000..21c5fdf --- /dev/null +++ b/docs/components/form/__demos__/form-item/itemSpan.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +import { SuperCheckboxGroup, SuperEmail, SuperForm, SuperInput, SuperNumber } from 'super-antd'; + +const Demo = () => { + return ( + + + + + + + + ); +}; + +export default Demo; diff --git a/docs/components/form/__demos__/form-item/types.tsx b/docs/components/form/__demos__/form-item/types.tsx index c774212..43a4bfb 100644 --- a/docs/components/form/__demos__/form-item/types.tsx +++ b/docs/components/form/__demos__/form-item/types.tsx @@ -21,6 +21,8 @@ export interface FormItemProps { * | number | string */ wrapperCol?: ColType; + /** 表单项占用的栅格数。 */ + itemSpan?: number; /** 只读。 */ readonly?: boolean; /** 禁用。 */ diff --git a/docs/components/form/__demos__/form/itemCount.tsx b/docs/components/form/__demos__/form/itemCount.tsx new file mode 100644 index 0000000..04f291a --- /dev/null +++ b/docs/components/form/__demos__/form/itemCount.tsx @@ -0,0 +1,35 @@ +import { Radio } from 'antd'; +import React, { useState } from 'react'; + +import { SuperCheckboxGroup, SuperEmail, SuperForm, SuperInput, SuperNumber } from 'super-antd'; + +const Demo = () => { + const [itemCount, setItemCount] = useState(3); + return ( + <> +
+ setItemCount(Number(e.target.value))} + options={[ + { value: 1, label: '一列' }, + { value: 2, label: '两列' }, + { value: 3, label: '三列' }, + ]} + > +
+ + + + + + + + + + ); +}; + +export default Demo; diff --git a/docs/components/form/__demos__/form/types.tsx b/docs/components/form/__demos__/form/types.tsx index f6eb439..2545e88 100644 --- a/docs/components/form/__demos__/form/types.tsx +++ b/docs/components/form/__demos__/form/types.tsx @@ -79,6 +79,8 @@ export interface IFormDemoProps { btns?: BtnsProps; /** 对齐方式 */ align?: 'left' | 'right' | 'center'; + /** 一列展示表单项的个数 */ + itemCount?: number; } const Demo: FC = () => <>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"