first commit
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import React, {useCallback, useMemo} from 'react';
|
||||
import {
|
||||
Form,
|
||||
Button,
|
||||
Space, Row, Col,
|
||||
} from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { SearchFormProps, SubmitterButton } from './typings';
|
||||
import type { FormItemProps } from 'antd';
|
||||
import type { FormColumn } from "@/components/XinFormField/FieldRender";
|
||||
import FieldRender from '@/components/XinFormField/FieldRender';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
/**
|
||||
* SearchForm - JSON 配置动态搜索表单组件
|
||||
*/
|
||||
function SearchForm<T extends Record<string, any> = any>(props: SearchFormProps<T>) {
|
||||
const {
|
||||
columns,
|
||||
handleSearch,
|
||||
submitter,
|
||||
form: formRef,
|
||||
grid = true,
|
||||
rowProps = {
|
||||
gutter: [20, 20],
|
||||
wrap: true
|
||||
},
|
||||
colProps = {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
md: 12,
|
||||
lg: 8,
|
||||
xl: 6,
|
||||
xxl: 4
|
||||
},
|
||||
...formProps
|
||||
} = props;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm<T>(formRef);
|
||||
|
||||
// 渲染表单项
|
||||
const renderFormItem = useCallback((column: FormColumn<T>, index: number): React.ReactNode => {
|
||||
const {
|
||||
dataIndex,
|
||||
valueType,
|
||||
title = '',
|
||||
fieldProps = {},
|
||||
fieldRender
|
||||
} = column;
|
||||
|
||||
// Form.Item 允许的属性列表
|
||||
const formItemPropKeys = [
|
||||
'colon', 'extra', 'getValueFromEvent', 'help', 'htmlFor',
|
||||
'initialValue', 'labelAlign', 'labelCol', 'name', 'normalize',
|
||||
'noStyle', 'tooltip', 'wrapperCol', 'layout'
|
||||
];
|
||||
const formItemProps = pick(column, formItemPropKeys);
|
||||
|
||||
const key = String(dataIndex) || `form-item-${index}`;
|
||||
|
||||
const defaultFieldRender = (
|
||||
<FieldRender
|
||||
valueType={valueType}
|
||||
placeholder={title}
|
||||
{...fieldProps}
|
||||
/>
|
||||
);
|
||||
|
||||
const formItemContent = (
|
||||
<Form.Item
|
||||
style={{marginBottom: 0}}
|
||||
key={key}
|
||||
name={key}
|
||||
label={column.title}
|
||||
{...formItemProps as FormItemProps}
|
||||
required={false}
|
||||
>
|
||||
{fieldRender ? fieldRender(form) : defaultFieldRender}
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
return grid ? <Col {...colProps} {...column.colProps} key={key}>{formItemContent}</Col> : formItemContent;
|
||||
}, [form, grid, colProps]);
|
||||
|
||||
// 渲染提交按钮
|
||||
const renderSubmitter = useMemo(() => {
|
||||
if (submitter?.render === false) return null;
|
||||
|
||||
const submitButton = (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => form.submit()}
|
||||
{...submitter?.submitButtonProps}
|
||||
>
|
||||
{submitter?.submitText || t('xin.table.search.search')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
const resetButton = (
|
||||
<Button
|
||||
onClick={() => form.resetFields()}
|
||||
{...submitter?.resetButtonProps}
|
||||
>
|
||||
{submitter?.resetText || t('xin.table.search.reset')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
const buttons: SubmitterButton = {
|
||||
search: submitButton,
|
||||
reset: resetButton
|
||||
};
|
||||
|
||||
if (typeof submitter?.render === 'function') {
|
||||
return submitter.render(buttons);
|
||||
}
|
||||
|
||||
return (
|
||||
<Form.Item style={{marginBottom: 0}}>
|
||||
<Space size={16}>
|
||||
{buttons.reset}
|
||||
{buttons.search}
|
||||
</Space>
|
||||
</Form.Item>
|
||||
);
|
||||
}, [form, submitter, t]);
|
||||
|
||||
// 表单内容
|
||||
return (
|
||||
<Form
|
||||
{...formProps}
|
||||
form={form}
|
||||
onFinish={handleSearch}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{grid ? (
|
||||
<Row {...rowProps}>
|
||||
{columns.map((column, index) => renderFormItem(column, index))}
|
||||
<Col {...colProps}>{renderSubmitter}</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<>
|
||||
{columns.map((column, index) => renderFormItem(column, index))}
|
||||
{renderSubmitter}
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchForm;
|
||||
export type { SearchFormProps };
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { ButtonProps, ColProps, FormInstance, FormProps, RowProps } from "antd";
|
||||
import type { ReactNode } from "react";
|
||||
import type { FormColumn } from "@/components/XinFormField/FieldRender";
|
||||
|
||||
/**
|
||||
* 操作栏按钮
|
||||
*/
|
||||
export type SubmitterButton = {
|
||||
/** 查询按钮 */
|
||||
search: ReactNode;
|
||||
/** 重置按钮 */
|
||||
reset: ReactNode;
|
||||
/** 折叠按钮 */
|
||||
collapse?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单操作栏属性
|
||||
*/
|
||||
export interface SubmitterProps {
|
||||
/** 操作栏渲染 */
|
||||
render?: false | ((dom: SubmitterButton) => ReactNode);
|
||||
/** 提交按钮文本 */
|
||||
submitText?: string | ReactNode;
|
||||
/** 重置按钮文本 */
|
||||
resetText?: string | ReactNode;
|
||||
/** 提交按钮属性 */
|
||||
submitButtonProps?: Omit<ButtonProps, 'loading' | 'onClick'>;
|
||||
/** 重置按钮属性 */
|
||||
resetButtonProps?: Omit<ButtonProps, 'loading' | 'onClick'>;
|
||||
/** 折叠按钮渲染 */
|
||||
collapseRender?: (collapse: boolean) => ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* XinSearch 组件属性
|
||||
*/
|
||||
export type SearchFormProps<T = any> = Omit<FormProps<T>, 'onFinish' | 'form'> & {
|
||||
/** 表单列配置 */
|
||||
columns: FormColumn<T>[];
|
||||
/** 表单实例引用 */
|
||||
form?: FormInstance<T>;
|
||||
/** 表单提交 */
|
||||
handleSearch?: (values: T) => Promise<boolean | void>;
|
||||
/** 渲染表单操作栏 */
|
||||
submitter?: SubmitterProps;
|
||||
/** 是否使用 Grid 布局 */
|
||||
grid?: boolean;
|
||||
/** 开启 grid 模式时传递给 Row */
|
||||
rowProps?: RowProps;
|
||||
/** 传递给表单项的 Col */
|
||||
colProps?: ColProps;
|
||||
}
|
||||
Reference in New Issue
Block a user