mirror of https://github.com/msgbyte/tailchat
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
import React, { useMemo, useState } from 'react';
|
|
import { useFormik } from 'formik';
|
|
import _isNil from 'lodash/isNil';
|
|
import _fromPairs from 'lodash/fromPairs';
|
|
import _isFunction from 'lodash/isFunction';
|
|
import _isEmpty from 'lodash/isEmpty';
|
|
import type { ObjectSchema } from 'yup';
|
|
import { FastFormContext } from './context';
|
|
import { getField, regField } from './field';
|
|
import type {
|
|
FastFormFieldComponent,
|
|
FastFormFieldProps,
|
|
FastFormFieldMeta,
|
|
} from './field';
|
|
import { getFormContainer } from './container';
|
|
|
|
/**
|
|
* 表单配置
|
|
*/
|
|
export interface FastFormProps {
|
|
fields: FastFormFieldMeta[]; // 字段详情
|
|
schema?: ObjectSchema<any>; // yup schame object 用于表单校验
|
|
layout?: 'horizontal' | 'vertical'; // 布局方式(默认水平)
|
|
submitLabel?: string; // 提交按钮的标签名
|
|
onSubmit: (values: any) => Promise<void> | void; // 点击提交按钮的回调
|
|
onChange?: (values: any) => void; // 数据更新回调
|
|
}
|
|
|
|
/**
|
|
* 一个快速生成表单的组件
|
|
* 用于通过配置来生成表单,简化通用代码
|
|
*/
|
|
export const FastForm: React.FC<FastFormProps> = React.memo((props) => {
|
|
const initialValues = useMemo(() => {
|
|
return _fromPairs(
|
|
props.fields.map((field) => [field.name, field.defaultValue ?? ''])
|
|
);
|
|
}, [props.fields]);
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const formik = useFormik({
|
|
initialValues,
|
|
validationSchema: props.schema,
|
|
onSubmit: async (values) => {
|
|
setLoading(true);
|
|
try {
|
|
_isFunction(props.onSubmit) && (await props.onSubmit(values));
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
},
|
|
validate: (values) => {
|
|
_isFunction(props.onChange) && props.onChange(values);
|
|
},
|
|
});
|
|
const { handleSubmit, setFieldValue, values, errors } = formik;
|
|
|
|
const FastFormContainer = getFormContainer();
|
|
|
|
if (_isNil(FastFormContainer)) {
|
|
console.warn('FastFormContainer 没有被注册');
|
|
return null;
|
|
}
|
|
|
|
const fieldsRender = useMemo(() => {
|
|
return props.fields.map((fieldMeta, i) => {
|
|
const fieldName = fieldMeta.name;
|
|
const value = values[fieldName];
|
|
const error = errors[fieldName];
|
|
const Component = getField(fieldMeta.type);
|
|
|
|
if (_isNil(Component)) {
|
|
return null;
|
|
} else {
|
|
return (
|
|
<Component
|
|
key={fieldName + i}
|
|
{...fieldMeta}
|
|
value={value}
|
|
error={error}
|
|
onChange={(val: any) => setFieldValue(fieldName, val)}
|
|
/>
|
|
);
|
|
}
|
|
});
|
|
}, [props.fields, values, errors, setFieldValue]);
|
|
|
|
return (
|
|
<FastFormContext.Provider value={formik}>
|
|
<FastFormContainer
|
|
loading={loading}
|
|
layout={props.layout ?? 'horizontal'}
|
|
submitLabel={props.submitLabel}
|
|
handleSubmit={handleSubmit}
|
|
canSubmit={_isEmpty(errors)}
|
|
>
|
|
{fieldsRender}
|
|
</FastFormContainer>
|
|
</FastFormContext.Provider>
|
|
);
|
|
});
|
|
FastForm.displayName = 'FastForm';
|
|
FastForm.defaultProps = {
|
|
submitLabel: '提交',
|
|
};
|
|
|
|
export { CustomField } from './CustomField';
|
|
export type { FastFormFieldComponent, FastFormFieldProps, FastFormFieldMeta };
|
|
export { regField };
|
|
export { regFormContainer } from './container';
|
|
export type { FastFormContainerComponent } from './container';
|