index.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import { View, Text } from '@tarojs/components'
  2. import { useState, useEffect } from 'react'
  3. import Taro from '@tarojs/taro'
  4. import { useAuth } from '@/utils/auth'
  5. import { cn } from '@/utils/cn'
  6. import { Button } from '@/components/ui/button'
  7. import { Input } from '@/components/ui/input'
  8. import Navbar from '@/components/ui/navbar'
  9. import './index.css'
  10. export default function Register() {
  11. const [username, setUsername] = useState('')
  12. const [email, setEmail] = useState('')
  13. const [password, setPassword] = useState('')
  14. const [confirmPassword, setConfirmPassword] = useState('')
  15. const [showPassword, setShowPassword] = useState(false)
  16. const [showConfirmPassword, setShowConfirmPassword] = useState(false)
  17. const { register, isLoading } = useAuth()
  18. useEffect(() => {
  19. Taro.setNavigationBarTitle({
  20. title: '创建账号'
  21. })
  22. }, [])
  23. const handleRegister = async () => {
  24. // 输入验证
  25. if (!username.trim()) {
  26. Taro.showToast({
  27. title: '请输入用户名',
  28. icon: 'none',
  29. duration: 2000
  30. })
  31. return
  32. }
  33. if (username.trim().length < 3) {
  34. Taro.showToast({
  35. title: '用户名至少3个字符',
  36. icon: 'none',
  37. duration: 2000
  38. })
  39. return
  40. }
  41. if (!password.trim()) {
  42. Taro.showToast({
  43. title: '请输入密码',
  44. icon: 'none',
  45. duration: 2000
  46. })
  47. return
  48. }
  49. if (password.trim().length < 6) {
  50. Taro.showToast({
  51. title: '密码至少6个字符',
  52. icon: 'none',
  53. duration: 2000
  54. })
  55. return
  56. }
  57. if (password.trim() !== confirmPassword.trim()) {
  58. Taro.showToast({
  59. title: '两次输入的密码不一致',
  60. icon: 'none',
  61. duration: 2000
  62. })
  63. return
  64. }
  65. if (email.trim() && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim())) {
  66. Taro.showToast({
  67. title: '请输入有效的邮箱地址',
  68. icon: 'none',
  69. duration: 2000
  70. })
  71. return
  72. }
  73. try {
  74. Taro.showLoading({
  75. title: '注册中...',
  76. mask: true
  77. })
  78. await register({
  79. username: username.trim(),
  80. password: password.trim(),
  81. email: email.trim() || undefined
  82. })
  83. Taro.hideLoading()
  84. Taro.showToast({
  85. title: '注册成功',
  86. icon: 'success',
  87. duration: 1500
  88. })
  89. setTimeout(() => {
  90. Taro.switchTab({ url: '/pages/index/index' })
  91. }, 1500)
  92. } catch (error: any) {
  93. Taro.hideLoading()
  94. const errorMessage = error.message || '注册失败,请重试'
  95. Taro.showToast({
  96. title: errorMessage,
  97. icon: 'none',
  98. duration: 3000
  99. })
  100. }
  101. }
  102. const goToLogin = () => {
  103. Taro.navigateBack()
  104. }
  105. return (
  106. <View className="min-h-screen bg-gradient-to-br from-green-50 via-white to-emerald-50">
  107. <Navbar
  108. title="创建账号"
  109. backgroundColor="bg-transparent"
  110. textColor="text-gray-900"
  111. border={false}
  112. />
  113. <View className="flex-1 px-6 py-12">
  114. {/* Logo区域 */}
  115. <View className="flex flex-col items-center mb-10">
  116. <View className="w-20 h-20 mb-4 rounded-full bg-white shadow-lg flex items-center justify-center">
  117. <View className="i-heroicons-user-plus-20-solid w-12 h-12 text-green-500" />
  118. </View>
  119. <Text className="text-2xl font-bold text-gray-900 mb-1">创建账号</Text>
  120. <Text className="text-gray-600 text-sm">欢迎加入我们的小程序社区</Text>
  121. </View>
  122. {/* 注册表单 */}
  123. <View className="bg-white rounded-2xl shadow-sm p-6">
  124. <View className="space-y-5">
  125. {/* 用户名输入框 */}
  126. <View className="space-y-2">
  127. <Text className="text-sm font-medium text-gray-700">用户名</Text>
  128. <Input
  129. leftIcon="i-heroicons-user-20-solid"
  130. placeholder="请输入用户名(3-20个字符)"
  131. value={username}
  132. onInput={(e) => setUsername(e.detail.value)}
  133. maxlength={20}
  134. size="lg"
  135. variant="filled"
  136. />
  137. </View>
  138. {/* 邮箱输入框 */}
  139. <View className="space-y-2">
  140. <Text className="text-sm font-medium text-gray-700">邮箱(可选)</Text>
  141. <Input
  142. leftIcon="i-heroicons-envelope-20-solid"
  143. placeholder="请输入邮箱地址"
  144. type="text"
  145. value={email}
  146. onInput={(e) => setEmail(e.detail.value)}
  147. maxlength={50}
  148. size="lg"
  149. variant="filled"
  150. />
  151. </View>
  152. {/* 密码输入框 */}
  153. <View className="space-y-2">
  154. <Text className="text-sm font-medium text-gray-700">密码</Text>
  155. <Input
  156. leftIcon="i-heroicons-lock-closed-20-solid"
  157. rightIcon={showPassword ? "i-heroicons-eye-20-solid" : "i-heroicons-eye-slash-20-solid"}
  158. placeholder="请输入密码(至少6位)"
  159. password={!showPassword}
  160. value={password}
  161. onInput={(e) => setPassword(e.detail.value)}
  162. maxlength={20}
  163. size="lg"
  164. variant="filled"
  165. onRightIconClick={() => setShowPassword(!showPassword)}
  166. />
  167. </View>
  168. {/* 确认密码输入框 */}
  169. <View className="space-y-2">
  170. <Text className="text-sm font-medium text-gray-700">确认密码</Text>
  171. <Input
  172. leftIcon="i-heroicons-lock-closed-20-solid"
  173. rightIcon={showConfirmPassword ? "i-heroicons-eye-20-solid" : "i-heroicons-eye-slash-20-solid"}
  174. placeholder="请再次输入密码"
  175. password={!showConfirmPassword}
  176. value={confirmPassword}
  177. onInput={(e) => setConfirmPassword(e.detail.value)}
  178. maxlength={20}
  179. size="lg"
  180. variant="filled"
  181. onRightIconClick={() => setShowConfirmPassword(!showConfirmPassword)}
  182. />
  183. </View>
  184. {/* 注册按钮 */}
  185. <Button
  186. className={cn(
  187. "w-full",
  188. isLoading || !username || !password || !confirmPassword
  189. ? "bg-gray-300"
  190. : "bg-green-500 hover:bg-green-600"
  191. )}
  192. size="lg"
  193. variant="default"
  194. onClick={handleRegister}
  195. disabled={isLoading || !username || !password || !confirmPassword}
  196. >
  197. {isLoading ? (
  198. <View className="flex items-center justify-center">
  199. <View className="i-heroicons-arrow-path-20-solid animate-spin w-5 h-5 mr-2" />
  200. 注册中...
  201. </View>
  202. ) : (
  203. '立即注册'
  204. )}
  205. </Button>
  206. </View>
  207. {/* 登录链接 */}
  208. <View className="mt-6 text-center">
  209. <Text className="text-sm text-gray-600">
  210. 已有账号?
  211. <Text
  212. className="text-green-500 font-medium hover:text-green-600"
  213. onClick={goToLogin}
  214. >
  215. 立即登录
  216. </Text>
  217. </Text>
  218. </View>
  219. </View>
  220. {/* 协议声明 */}
  221. <View className="mt-8 text-center">
  222. <Text className="text-xs text-gray-500">
  223. 注册即表示您同意
  224. <Text className="text-green-500 mx-1">用户协议</Text>
  225. <Text className="text-green-500 mx-1">隐私政策</Text>
  226. </Text>
  227. </View>
  228. </View>
  229. </View>
  230. )
  231. }