index.tsx 9.4 KB

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