Browse Source

✨ feat(register): 重构注册表单使用react-hook-form

- 集成react-hook-form实现表单管理与验证
- 定义RegisterFormData接口规范表单数据结构
- 实现用户名、密码、邮箱等字段的表单验证规则
- 移除手动状态管理,使用form.handleSubmit处理表单提交
- 优化表单UI组件结构,使用FormField、FormItem等组件包装输入框
- 增强表单验证规则:用户名3-20字符、密码6-20字符、邮箱格式验证、密码一致性检查
- 改进表单提交逻辑,直接使用表单数据而非独立状态变量
yourname 4 tháng trước cách đây
mục cha
commit
1e480eb064
1 tập tin đã thay đổi với 168 bổ sung144 xóa
  1. 168 144
      mini/src/pages/register/index.tsx

+ 168 - 144
mini/src/pages/register/index.tsx

@@ -1,21 +1,36 @@
 import { View, Text } from '@tarojs/components'
-import { useState, useEffect } from 'react'
+import { useEffect, useState } from 'react'
+import { useForm } from 'react-hook-form'
 import Taro from '@tarojs/taro'
 import { useAuth } from '@/utils/auth'
 import { cn } from '@/utils/cn'
 import { Button } from '@/components/ui/button'
 import { Input } from '@/components/ui/input'
+import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'
 import Navbar from '@/components/ui/navbar'
 import './index.css'
 
+interface RegisterFormData {
+  username: string
+  email?: string
+  password: string
+  confirmPassword: string
+}
+
 export default function Register() {
-  const [username, setUsername] = useState('')
-  const [email, setEmail] = useState('')
-  const [password, setPassword] = useState('')
-  const [confirmPassword, setConfirmPassword] = useState('')
+  const { register, isLoading } = useAuth()
+  
+  const form = useForm<RegisterFormData>({
+    defaultValues: {
+      username: '',
+      email: '',
+      password: '',
+      confirmPassword: ''
+    }
+  })
+
   const [showPassword, setShowPassword] = useState(false)
   const [showConfirmPassword, setShowConfirmPassword] = useState(false)
-  const { register, isLoading } = useAuth()
 
   useEffect(() => {
     Taro.setNavigationBarTitle({
@@ -23,62 +38,7 @@ export default function Register() {
     })
   }, [])
 
-  const handleRegister = async () => {
-    // 输入验证
-    if (!username.trim()) {
-      Taro.showToast({
-        title: '请输入用户名',
-        icon: 'none',
-        duration: 2000
-      })
-      return
-    }
-
-    if (username.trim().length < 3) {
-      Taro.showToast({
-        title: '用户名至少3个字符',
-        icon: 'none',
-        duration: 2000
-      })
-      return
-    }
-
-    if (!password.trim()) {
-      Taro.showToast({
-        title: '请输入密码',
-        icon: 'none',
-        duration: 2000
-      })
-      return
-    }
-
-    if (password.trim().length < 6) {
-      Taro.showToast({
-        title: '密码至少6个字符',
-        icon: 'none',
-        duration: 2000
-      })
-      return
-    }
-
-    if (password.trim() !== confirmPassword.trim()) {
-      Taro.showToast({
-        title: '两次输入的密码不一致',
-        icon: 'none',
-        duration: 2000
-      })
-      return
-    }
-
-    if (email.trim() && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) {
-      Taro.showToast({
-        title: '请输入有效的邮箱地址',
-        icon: 'none',
-        duration: 2000
-      })
-      return
-    }
-
+  const handleRegister = async (data: RegisterFormData) => {
     try {
       Taro.showLoading({
         title: '注册中...',
@@ -86,9 +46,9 @@ export default function Register() {
       })
 
       await register({
-        username: username.trim(),
-        password: password.trim(),
-        email: email.trim() || undefined
+        username: data.username.trim(),
+        password: data.password.trim(),
+        email: data.email?.trim() || undefined
       })
       
       Taro.hideLoading()
@@ -138,93 +98,157 @@ export default function Register() {
 
         {/* 注册表单 */}
         <View className="bg-white rounded-2xl shadow-sm p-6">
-          <View className="space-y-5">
-            {/* 用户名输入框 */}
-            <View className="space-y-2">
-              <Text className="text-sm font-medium text-gray-700">用户名</Text>
-              <Input
-                leftIcon="i-heroicons-user-20-solid"
-                placeholder="请输入用户名(3-20个字符)"
-                value={username}
-                onInput={(e) => setUsername(e.detail.value)}
-                maxlength={20}
-                size="lg"
-                variant="filled"
+          <Form {...form}>
+            <View className="space-y-5">
+              {/* 用户名输入框 */}
+              <FormField
+                control={form.control}
+                name="username"
+                rules={{
+                  required: "请输入用户名",
+                  minLength: {
+                    value: 3,
+                    message: "用户名至少3个字符"
+                  },
+                  maxLength: {
+                    value: 20,
+                    message: "用户名最多20个字符"
+                  }
+                }}
+                render={({ field }) => (
+                  <FormItem>
+                    <FormLabel>用户名</FormLabel>
+                    <FormControl>
+                      <Input
+                        leftIcon="i-heroicons-user-20-solid"
+                        placeholder="请输入用户名(3-20个字符)"
+                        maxlength={20}
+                        size="lg"
+                        variant="filled"
+                        {...field}
+                      />
+                    </FormControl>
+                    <FormMessage />
+                  </FormItem>
+                )}
               />
-            </View>
 
-            {/* 邮箱输入框 */}
-            <View className="space-y-2">
-              <Text className="text-sm font-medium text-gray-700">邮箱(可选)</Text>
-              <Input
-                leftIcon="i-heroicons-envelope-20-solid"
-                placeholder="请输入邮箱地址"
-                type="text"
-                value={email}
-                onInput={(e) => setEmail(e.detail.value)}
-                maxlength={50}
-                size="lg"
-                variant="filled"
+              {/* 邮箱输入框 */}
+              <FormField
+                control={form.control}
+                name="email"
+                rules={{
+                  pattern: {
+                    value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
+                    message: "请输入有效的邮箱地址"
+                  }
+                }}
+                render={({ field }) => (
+                  <FormItem>
+                    <FormLabel>邮箱(可选)</FormLabel>
+                    <FormControl>
+                      <Input
+                        leftIcon="i-heroicons-envelope-20-solid"
+                        placeholder="请输入邮箱地址"
+                        type="text"
+                        maxlength={50}
+                        size="lg"
+                        variant="filled"
+                        {...field}
+                      />
+                    </FormControl>
+                    <FormMessage />
+                  </FormItem>
+                )}
               />
-            </View>
 
-            {/* 密码输入框 */}
-            <View className="space-y-2">
-              <Text className="text-sm font-medium text-gray-700">密码</Text>
-              <Input
-                leftIcon="i-heroicons-lock-closed-20-solid"
-                rightIcon={showPassword ? "i-heroicons-eye-20-solid" : "i-heroicons-eye-slash-20-solid"}
-                placeholder="请输入密码(至少6位)"
-                password={!showPassword}
-                value={password}
-                onInput={(e) => setPassword(e.detail.value)}
-                maxlength={20}
-                size="lg"
-                variant="filled"
-                onRightIconClick={() => setShowPassword(!showPassword)}
+              {/* 密码输入框 */}
+              <FormField
+                control={form.control}
+                name="password"
+                rules={{
+                  required: "请输入密码",
+                  minLength: {
+                    value: 6,
+                    message: "密码至少6位"
+                  },
+                  maxLength: {
+                    value: 20,
+                    message: "密码最多20位"
+                  }
+                }}
+                render={({ field }) => (
+                  <FormItem>
+                    <FormLabel>密码</FormLabel>
+                    <FormControl>
+                      <Input
+                        leftIcon="i-heroicons-lock-closed-20-solid"
+                        rightIcon={showPassword ? "i-heroicons-eye-20-solid" : "i-heroicons-eye-slash-20-solid"}
+                        placeholder="请输入密码(至少6位)"
+                        password={!showPassword}
+                        maxlength={20}
+                        size="lg"
+                        variant="filled"
+                        onRightIconClick={() => setShowPassword(!showPassword)}
+                        {...field}
+                      />
+                    </FormControl>
+                    <FormMessage />
+                  </FormItem>
+                )}
               />
-            </View>
 
-            {/* 确认密码输入框 */}
-            <View className="space-y-2">
-              <Text className="text-sm font-medium text-gray-700">确认密码</Text>
-              <Input
-                leftIcon="i-heroicons-lock-closed-20-solid"
-                rightIcon={showConfirmPassword ? "i-heroicons-eye-20-solid" : "i-heroicons-eye-slash-20-solid"}
-                placeholder="请再次输入密码"
-                password={!showConfirmPassword}
-                value={confirmPassword}
-                onInput={(e) => setConfirmPassword(e.detail.value)}
-                maxlength={20}
-                size="lg"
-                variant="filled"
-                onRightIconClick={() => setShowConfirmPassword(!showConfirmPassword)}
+              {/* 确认密码输入框 */}
+              <FormField
+                control={form.control}
+                name="confirmPassword"
+                rules={{
+                  required: "请再次输入密码",
+                  validate: (value) => value === form.getValues("password") || "两次输入的密码不一致"
+                }}
+                render={({ field }) => (
+                  <FormItem>
+                    <FormLabel>确认密码</FormLabel>
+                    <FormControl>
+                      <Input
+                        leftIcon="i-heroicons-lock-closed-20-solid"
+                        rightIcon={showConfirmPassword ? "i-heroicons-eye-20-solid" : "i-heroicons-eye-slash-20-solid"}
+                        placeholder="请再次输入密码"
+                        password={!showConfirmPassword}
+                        maxlength={20}
+                        size="lg"
+                        variant="filled"
+                        onRightIconClick={() => setShowConfirmPassword(!showConfirmPassword)}
+                        {...field}
+                      />
+                    </FormControl>
+                    <FormMessage />
+                  </FormItem>
+                )}
               />
-            </View>
 
-            {/* 注册按钮 */}
-            <Button
-              className={cn(
-                "w-full",
-                isLoading || !username || !password || !confirmPassword
-                  ? "bg-gray-300"
-                  : "bg-green-500 hover:bg-green-600"
-              )}
-              size="lg"
-              variant="default"
-              onClick={handleRegister}
-              disabled={isLoading || !username || !password || !confirmPassword}
-            >
-              {isLoading ? (
-                <View className="flex items-center justify-center">
-                  <View className="i-heroicons-arrow-path-20-solid animate-spin w-5 h-5 mr-2" />
-                  注册中...
-                </View>
-              ) : (
-                '立即注册'
-              )}
-            </Button>
-          </View>
+              {/* 注册按钮 */}
+              <Button
+                className={cn(
+                  "w-full",
+                  isLoading ? "bg-gray-300" : "bg-green-500 hover:bg-green-600"
+                )}
+                size="lg"
+                variant="default"
+                onClick={form.handleSubmit(handleRegister) as any}
+                disabled={isLoading}
+              >
+                {isLoading ? (
+                  <View className="flex items-center justify-center">
+                    <View className="i-heroicons-arrow-path-20-solid animate-spin w-5 h-5 mr-2" />
+                    注册中...
+                  </View>
+                ) : (
+                  '立即注册'
+                )}
+              </Button>
+            </View>
+          </Form>
 
           {/* 登录链接 */}
           <View className="mt-6 text-center">