u
This commit is contained in:
1 parent
69d03709ba
commit
44fb48ef6a
9 files changed
+625
-25
No files matched your search
@@ -1,10 +1,149 @@
|
||||
import { Text, View } from 'react-native'
|
||||
import type { FormWrapperRef } from '@/components/common/FormWrapper'
|
||||
import { Lock, UserRound } from 'lucide-react-native'
|
||||
import { useRef, useState } from 'react'
|
||||
import { Image, View } from 'react-native'
|
||||
import FormWrapper from '@/components/common/FormWrapper'
|
||||
import { Button, ButtonSpinner, ButtonText } from '@/components/ui/button'
|
||||
import { useToast } from '@/utils/toast'
|
||||
|
||||
interface RegisterForm {
|
||||
username: string
|
||||
password: string
|
||||
confirmPassword: string
|
||||
a: string
|
||||
b: string
|
||||
c: string
|
||||
}
|
||||
|
||||
export default function AuthLogin() {
|
||||
const toast = useToast()
|
||||
const [form, setForm] = useState<RegisterForm>({
|
||||
username: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
a: '',
|
||||
b: '',
|
||||
c: '',
|
||||
})
|
||||
const [loading, setLoading] = useState(false)
|
||||
const formRef = useRef<FormWrapperRef>(null)
|
||||
|
||||
const handleInputChange = (field: keyof RegisterForm, value: string) => {
|
||||
setForm(prev => ({ ...prev, [field]: value }))
|
||||
}
|
||||
|
||||
const defaultLogin = () => {
|
||||
if (formRef.current?.validate()) {
|
||||
setLoading(true)
|
||||
toast.show({ title: '校验通过,开始注册', action: 'success' })
|
||||
// 模拟注册过程
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
export default function Register() {
|
||||
return (
|
||||
<View style={{ minHeight: 1000 }}>
|
||||
<Text>测试内容</Text>
|
||||
<View style={{ height: 800, backgroundColor: 'red' }} />
|
||||
<View className="flex flex-col items-center px-6 h-full mb-4">
|
||||
<Image source={require('@/static/images/logo.png')} style={{ width: 120, height: 120 }} className="my-12" />
|
||||
|
||||
<FormWrapper ref={formRef} form={form}>
|
||||
<FormWrapper.FormItem
|
||||
name="username"
|
||||
label="账号"
|
||||
required
|
||||
>
|
||||
<FormWrapper.Input
|
||||
placeholder="请输入账号"
|
||||
leftIcon={UserRound}
|
||||
value={form.username}
|
||||
onChangeText={text => handleInputChange('username', text)}
|
||||
/>
|
||||
</FormWrapper.FormItem>
|
||||
|
||||
<FormWrapper.FormItem
|
||||
name="password"
|
||||
label="密码"
|
||||
required
|
||||
rules={[
|
||||
(val: any) => val && !/\d{6}/.test(val) ? '请输入6位数字密码' : null,
|
||||
]}
|
||||
>
|
||||
<FormWrapper.Input
|
||||
placeholder="请输入密码"
|
||||
leftIcon={Lock}
|
||||
isPassword
|
||||
value={form.password}
|
||||
onChangeText={text => handleInputChange('password', text)}
|
||||
/>
|
||||
</FormWrapper.FormItem>
|
||||
|
||||
<FormWrapper.FormItem
|
||||
name="confirmPassword"
|
||||
label="确认密码"
|
||||
required
|
||||
rules={[
|
||||
(val: any) => val !== form.password ? '两次密码不一致' : null,
|
||||
]}
|
||||
>
|
||||
<FormWrapper.Input
|
||||
placeholder="请再次输入密码"
|
||||
leftIcon={Lock}
|
||||
isPassword
|
||||
value={form.confirmPassword}
|
||||
onChangeText={text => handleInputChange('confirmPassword', text)}
|
||||
/>
|
||||
</FormWrapper.FormItem>
|
||||
<FormWrapper.FormItem
|
||||
name="a"
|
||||
label="a"
|
||||
required
|
||||
>
|
||||
<FormWrapper.Input
|
||||
placeholder="请再次输入密码"
|
||||
isPassword
|
||||
value={form.a}
|
||||
onChangeText={text => handleInputChange('a', text)}
|
||||
/>
|
||||
</FormWrapper.FormItem>
|
||||
<FormWrapper.FormItem
|
||||
name="b"
|
||||
label="b"
|
||||
required
|
||||
>
|
||||
<FormWrapper.Input
|
||||
placeholder="请再次输入密码"
|
||||
isPassword
|
||||
value={form.a}
|
||||
onChangeText={text => handleInputChange('b', text)}
|
||||
/>
|
||||
</FormWrapper.FormItem>
|
||||
<FormWrapper.FormItem
|
||||
name="c"
|
||||
label="c"
|
||||
required
|
||||
>
|
||||
<FormWrapper.Input
|
||||
placeholder="请再次输入密码"
|
||||
isPassword
|
||||
value={form.c}
|
||||
onChangeText={text => handleInputChange('c', text)}
|
||||
/>
|
||||
</FormWrapper.FormItem>
|
||||
</FormWrapper>
|
||||
|
||||
<Button className="w-full mt-4" onPress={defaultLogin} disabled={loading}>
|
||||
{loading
|
||||
? (
|
||||
<>
|
||||
<ButtonSpinner color="gray" />
|
||||
<ButtonText>请稍等 . . .</ButtonText>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<ButtonText>注册</ButtonText>
|
||||
)}
|
||||
</Button>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
Reference in new issue
Block a user