|
|
@@ -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">
|