This commit is contained in:
oneao committed 2025-11-04 22:39:02 +08:00
1 parent 69d03709ba
commit 44fb48ef6a
9 files changed
+625 -25

No files matched your search

@@ -0,0 +1,210 @@
// FormWrapper.tsx
import React, { createContext, forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
import {
FormControl,
FormControlError,
FormControlErrorIcon,
FormControlErrorText,
FormControlLabel,
FormControlLabelText,
} from '@/components/ui/form-control'
import { AlertCircleIcon, EyeIcon, EyeOffIcon } from '@/components/ui/icon'
import { InputField, InputIcon, InputSlot, Input as NativeBaseInput } from '@/components/ui/input'
export interface FormWrapperRef {
validate: () => boolean
getValues: () => Record<string, any>
}
// 创建 FormContext 来传递表单数据和验证器注册函数
interface FormContextType<T = any> {
form: T
registerValidator: (name: string, validator: () => boolean) => void
unregisterValidator: (name: string) => void
}
const FormContext = createContext<FormContextType | undefined>(undefined)
// ---------------- FormItem ----------------
export interface FormItemProps<T extends Record<string, any> = any> {
name: keyof T
label?: string
required?: boolean
rules?: ((val: any) => string | null)[]
children: React.ReactNode
}
export function FormItem<T extends Record<string, any>>({
name,
label,
required,
rules,
children,
}: FormItemProps<T>) {
const formContext = useContext(FormContext)
if (!formContext) {
throw new Error('FormItem must be used within FormWrapper')
}
const { form, registerValidator, unregisterValidator } = formContext
const [error, setError] = useState('')
// 从 form 中根据 name 获取对应的值
const value = form[name]
const validate = (): boolean => {
let err = ''
if (required && (value === undefined || value === '' || value === null)) {
err = `${label || String(name)} 不能为空`
}
else if (rules) {
for (const rule of rules) {
const msg = rule(value)
if (msg) {
err = msg
break
}
}
}
setError(err)
return !err
}
// 注册验证函数到父组件
useEffect(() => {
registerValidator(String(name), validate)
return () => unregisterValidator(String(name))
}, [validate, registerValidator, unregisterValidator, name])
return (
<FormControl size="md" className="w-full mb-6" isInvalid={!!error}>
{label && (
<FormControlLabel>
<FormControlLabelText>{label}</FormControlLabelText>
</FormControlLabel>
)}
{children}
{error && (
<FormControlError>
<FormControlErrorIcon as={AlertCircleIcon} className="text-red-500" />
<FormControlErrorText className="text-red-500">{error}</FormControlErrorText>
</FormControlError>
)}
</FormControl>
)
}
// ---------------- FormInput ----------------
export interface FormInputProps {
placeholder?: string
leftIcon?: React.ComponentType<any>
isPassword?: boolean
value?: string
onChangeText?: (text: string) => void
}
export function FormInput({
placeholder,
leftIcon,
isPassword,
value,
onChangeText,
}: FormInputProps) {
const [showPassword, setShowPassword] = useState(false)
return (
<NativeBaseInput size="md">
{leftIcon && (
<InputSlot className="pl-3">
<InputIcon as={leftIcon} />
</InputSlot>
)}
<InputField
placeholder={placeholder}
type={isPassword ? (showPassword ? 'text' : 'password') : 'text'}
value={value}
onChangeText={onChangeText}
/>
{isPassword && (
<InputSlot className="pr-3" onPress={() => setShowPassword(prev => !prev)}>
<InputIcon as={showPassword ? EyeIcon : EyeOffIcon} />
</InputSlot>
)}
</NativeBaseInput>
)
}
// ---------------- FormWrapper ----------------
interface FormWrapperProps<T extends Record<string, any>> {
children: React.ReactNode
form: T
}
// 定义组件接口
interface FormWrapperComponent extends React.ForwardRefExoticComponent<
FormWrapperProps<any> & React.RefAttributes<FormWrapperRef>
> {
FormItem: typeof FormItem
Input: typeof FormInput
}
// 内部组件实现
function FormWrapperInner<T extends Record<string, any>>(
props: FormWrapperProps<T>,
ref: React.Ref<FormWrapperRef>,
) {
const { children, form } = props
const validators = useRef<Map<string, () => boolean>>(new Map())
const registerValidator = (name: string, validator: () => boolean) => {
validators.current.set(name, validator)
}
const unregisterValidator = (name: string) => {
validators.current.delete(name)
}
const validate = (): boolean => {
let isValid = true
// 然后调用所有验证函数
validators.current.forEach((validator) => {
if (!validator()) {
isValid = false
}
})
return isValid
}
const getValues = (): Record<string, any> => form
useImperativeHandle(ref, () => ({ validate, getValues }))
const contextValue: FormContextType<T> = {
form,
registerValidator,
unregisterValidator,
}
return (
<FormContext.Provider value={contextValue}>
{children}
</FormContext.Provider>
)
}
// 创建基础组件
const BaseFormWrapper = forwardRef(FormWrapperInner) as <T extends Record<string, any>>(
props: FormWrapperProps<T> & { ref?: React.Ref<FormWrapperRef> },
) => React.JSX.Element
// 创建带有静态属性的组件
const FormWrapper = BaseFormWrapper as FormWrapperComponent
// 添加静态属性
FormWrapper.FormItem = FormItem
FormWrapper.Input = FormInput
export default FormWrapper