index.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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 Login() {
  11. const [username, setUsername] = useState('')
  12. const [password, setPassword] = useState('')
  13. const [showPassword, setShowPassword] = useState(false)
  14. const { login, isLoading } = useAuth()
  15. // 设置导航栏标题
  16. useEffect(() => {
  17. Taro.setNavigationBarTitle({
  18. title: '用户登录'
  19. })
  20. }, [])
  21. const handleLogin = async () => {
  22. // 输入验证
  23. if (!username.trim()) {
  24. Taro.showToast({
  25. title: '请输入用户名',
  26. icon: 'none',
  27. duration: 2000
  28. })
  29. return
  30. }
  31. if (!password.trim()) {
  32. Taro.showToast({
  33. title: '请输入密码',
  34. icon: 'none',
  35. duration: 2000
  36. })
  37. return
  38. }
  39. if (username.trim().length < 3) {
  40. Taro.showToast({
  41. title: '用户名至少3个字符',
  42. icon: 'none',
  43. duration: 2000
  44. })
  45. return
  46. }
  47. if (password.trim().length < 6) {
  48. Taro.showToast({
  49. title: '密码至少6位',
  50. icon: 'none',
  51. duration: 2000
  52. })
  53. return
  54. }
  55. try {
  56. Taro.showLoading({
  57. title: '登录中...',
  58. mask: true
  59. })
  60. await login({
  61. username: username.trim(),
  62. password: password.trim()
  63. })
  64. Taro.hideLoading()
  65. Taro.showToast({
  66. title: '登录成功',
  67. icon: 'success',
  68. duration: 1500
  69. })
  70. setTimeout(() => {
  71. Taro.switchTab({ url: '/pages/index/index' })
  72. }, 1500)
  73. } catch (error: any) {
  74. Taro.hideLoading()
  75. const errorMessage = error.message || '登录失败'
  76. if (errorMessage.includes('用户名或密码错误')) {
  77. Taro.showToast({
  78. title: '用户名或密码错误',
  79. icon: 'none',
  80. duration: 3000
  81. })
  82. } else if (errorMessage.includes('网络')) {
  83. Taro.showModal({
  84. title: '网络错误',
  85. content: '请检查网络连接后重试',
  86. showCancel: false,
  87. confirmText: '确定'
  88. })
  89. } else {
  90. Taro.showToast({
  91. title: errorMessage,
  92. icon: 'none',
  93. duration: 3000
  94. })
  95. }
  96. }
  97. }
  98. const goToRegister = () => {
  99. Taro.navigateTo({
  100. url: '/pages/register/index'
  101. })
  102. }
  103. const handleUsernameInput = (e: any) => {
  104. const value = e.detail.value
  105. if (value.length <= 20) {
  106. setUsername(value.replace(/\s+/g, ''))
  107. }
  108. }
  109. const handlePasswordInput = (e: any) => {
  110. const value = e.detail.value
  111. if (value.length <= 20) {
  112. setPassword(value)
  113. }
  114. }
  115. return (
  116. <View className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-indigo-50">
  117. <Navbar
  118. title="用户登录"
  119. backgroundColor="bg-transparent"
  120. textColor="text-gray-900"
  121. border={false}
  122. />
  123. <View className="flex-1 px-6 py-12">
  124. {/* Logo区域 */}
  125. <View className="flex flex-col items-center mb-10">
  126. <View className="w-20 h-20 mb-4 rounded-full bg-white shadow-lg flex items-center justify-center">
  127. <View className="i-heroicons-user-circle-20-solid w-12 h-12 text-blue-500" />
  128. </View>
  129. <Text className="text-2xl font-bold text-gray-900 mb-1">欢迎回来</Text>
  130. <Text className="text-gray-600 text-sm">请使用您的账号登录系统</Text>
  131. </View>
  132. {/* 登录表单 */}
  133. <View className="bg-white rounded-2xl shadow-sm p-6">
  134. <View className="space-y-5">
  135. {/* 用户名输入框 */}
  136. <View className="space-y-2">
  137. <Text className="text-sm font-medium text-gray-700">用户名</Text>
  138. <Input
  139. leftIcon="i-heroicons-user-20-solid"
  140. placeholder="请输入用户名"
  141. value={username}
  142. onInput={handleUsernameInput}
  143. maxlength={20}
  144. type="text"
  145. confirmType="next"
  146. size="lg"
  147. variant="filled"
  148. />
  149. </View>
  150. {/* 密码输入框 */}
  151. <View className="space-y-2">
  152. <Text className="text-sm font-medium text-gray-700">密码</Text>
  153. <Input
  154. leftIcon="i-heroicons-lock-closed-20-solid"
  155. rightIcon={showPassword ? "i-heroicons-eye-20-solid" : "i-heroicons-eye-slash-20-solid"}
  156. placeholder="请输入密码"
  157. password={!showPassword}
  158. value={password}
  159. onInput={handlePasswordInput}
  160. maxlength={20}
  161. confirmType="done"
  162. size="lg"
  163. variant="filled"
  164. onRightIconClick={() => setShowPassword(!showPassword)}
  165. />
  166. </View>
  167. {/* 忘记密码 */}
  168. <View className="flex justify-end">
  169. <Text className="text-sm text-blue-500 hover:text-blue-600">忘记密码?</Text>
  170. </View>
  171. {/* 登录按钮 */}
  172. <Button
  173. className={cn(
  174. "w-full",
  175. isLoading || !username || !password
  176. ? "bg-gray-300"
  177. : "bg-blue-500 hover:bg-blue-600"
  178. )}
  179. size="lg"
  180. variant="default"
  181. onClick={handleLogin}
  182. disabled={isLoading || !username || !password}
  183. >
  184. {isLoading ? (
  185. <View className="flex items-center justify-center">
  186. <View className="i-heroicons-arrow-path-20-solid animate-spin w-5 h-5 mr-2" />
  187. 登录中...
  188. </View>
  189. ) : (
  190. '安全登录'
  191. )}
  192. </Button>
  193. </View>
  194. {/* 注册链接 */}
  195. <View className="mt-6 text-center">
  196. <Text className="text-sm text-gray-600">
  197. 还没有账号?
  198. <Text
  199. className="text-blue-500 font-medium hover:text-blue-600"
  200. onClick={goToRegister}
  201. >
  202. 立即注册
  203. </Text>
  204. </Text>
  205. </View>
  206. </View>
  207. {/* 协议声明 */}
  208. <View className="mt-8 text-center">
  209. <Text className="text-xs text-gray-500">
  210. 登录即表示您同意
  211. <Text className="text-blue-500 mx-1">用户协议</Text>
  212. <Text className="text-blue-500 mx-1">隐私政策</Text>
  213. </Text>
  214. </View>
  215. </View>
  216. </View>
  217. )
  218. }