LoginView.jsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import React, { useState } from 'react';
  2. import { useAuth } from '../context/AuthContext';
  3. import { Sparkles, ArrowRight, Lock, User, Mail, ShieldCheck } from 'lucide-react';
  4. export const LoginView = () => {
  5. const { login, register } = useAuth();
  6. const [isLoginTab, setIsLoginTab] = useState(true);
  7. const [identifier, setIdentifier] = useState('');
  8. const [password, setPassword] = useState('');
  9. const [username, setUsername] = useState('');
  10. const [loading, setLoading] = useState(false);
  11. const [errorMsg, setErrorMsg] = useState('');
  12. const handleSubmit = async (e) => {
  13. e.preventDefault();
  14. setErrorMsg('');
  15. setLoading(true);
  16. try {
  17. if (isLoginTab) {
  18. const res = await login(identifier, password);
  19. if (!res.success) {
  20. setErrorMsg(res.message || '登录失败,请检查账号与密码');
  21. }
  22. } else {
  23. if (!username.trim()) {
  24. setErrorMsg('请输入用户名');
  25. setLoading(false);
  26. return;
  27. }
  28. const res = await register(username, identifier, password);
  29. if (!res.success) {
  30. setErrorMsg(res.message || '注册失败,请稍后重试');
  31. }
  32. }
  33. } catch (err) {
  34. setErrorMsg('网络请求异常,请稍后重试');
  35. } finally {
  36. setLoading(false);
  37. }
  38. };
  39. return (
  40. <div className="min-h-screen flex items-center justify-center p-4 business-grid-bg">
  41. <div className="w-full max-w-md bg-white/90 dark:bg-slate-900/90 backdrop-blur-xl border border-black/10 dark:border-white/10 rounded-3xl p-8 shadow-2xl space-y-6 transition-all duration-300">
  42. {/* Header Branding */}
  43. <div className="text-center space-y-2">
  44. <img
  45. src="/app-icon.png"
  46. alt="星痕 App Logo"
  47. className="w-16 h-16 rounded-2xl object-cover border border-black/10 dark:border-white/20 mx-auto shadow-lg transform hover:scale-105 transition-transform"
  48. />
  49. <h1 className="text-2xl font-bold tracking-tight text-slate-900 dark:text-white">
  50. 星痕 CelestiaTrace
  51. </h1>
  52. <p className="text-xs text-slate-500 dark:text-slate-400">
  53. 云端现场记录极简同步平台
  54. </p>
  55. </div>
  56. {/* Tab Switcher */}
  57. <div className="flex bg-slate-100 dark:bg-slate-800/60 p-1 rounded-xl border border-black/5 dark:border-white/5">
  58. <button
  59. type="button"
  60. onClick={() => { setIsLoginTab(true); setErrorMsg(''); }}
  61. className={`flex-1 py-2 text-xs font-semibold rounded-lg transition-all ${
  62. isLoginTab
  63. ? 'bg-white dark:bg-slate-700 text-slate-900 dark:text-white shadow-sm'
  64. : 'text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white'
  65. }`}
  66. >
  67. 账号登录
  68. </button>
  69. <button
  70. type="button"
  71. onClick={() => { setIsLoginTab(false); setErrorMsg(''); }}
  72. className={`flex-1 py-2 text-xs font-semibold rounded-lg transition-all ${
  73. !isLoginTab
  74. ? 'bg-white dark:bg-slate-700 text-slate-900 dark:text-white shadow-sm'
  75. : 'text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white'
  76. }`}
  77. >
  78. 新用户注册
  79. </button>
  80. </div>
  81. {/* Error Alert */}
  82. {errorMsg && (
  83. <div className="p-3 text-xs rounded-xl bg-rose-500/10 border border-rose-500/20 text-rose-600 dark:text-rose-400 text-center font-medium animate-fade-in">
  84. {errorMsg}
  85. </div>
  86. )}
  87. {/* Form */}
  88. <form onSubmit={handleSubmit} className="space-y-4">
  89. {!isLoginTab && (
  90. <div className="space-y-1">
  91. <label className="text-xs font-medium text-slate-600 dark:text-slate-300">
  92. 用户名
  93. </label>
  94. <div className="relative">
  95. <User className="w-4 h-4 text-slate-400 absolute left-3 top-3" />
  96. <input
  97. type="text"
  98. required
  99. value={username}
  100. onChange={(e) => setUsername(e.target.value)}
  101. placeholder="例如: 张工"
  102. className="w-full pl-9 pr-4 py-2.5 rounded-xl bg-slate-50 dark:bg-slate-800 border border-black/10 dark:border-white/10 text-slate-900 dark:text-white text-xs focus:outline-none focus:border-slate-800 dark:focus:border-slate-200 transition-colors"
  103. />
  104. </div>
  105. </div>
  106. )}
  107. <div className="space-y-1">
  108. <label className="text-xs font-medium text-slate-600 dark:text-slate-300">
  109. 邮箱或手机号
  110. </label>
  111. <div className="relative">
  112. <Mail className="w-4 h-4 text-slate-400 absolute left-3 top-3" />
  113. <input
  114. type="text"
  115. required
  116. value={identifier}
  117. onChange={(e) => setIdentifier(e.target.value)}
  118. placeholder="user@example.com 或 13800138000"
  119. className="w-full pl-9 pr-4 py-2.5 rounded-xl bg-slate-50 dark:bg-slate-800 border border-black/10 dark:border-white/10 text-slate-900 dark:text-white text-xs focus:outline-none focus:border-slate-800 dark:focus:border-slate-200 transition-colors"
  120. />
  121. </div>
  122. </div>
  123. <div className="space-y-1">
  124. <label className="text-xs font-medium text-slate-600 dark:text-slate-300">
  125. 密码
  126. </label>
  127. <div className="relative">
  128. <Lock className="w-4 h-4 text-slate-400 absolute left-3 top-3" />
  129. <input
  130. type="password"
  131. required
  132. value={password}
  133. onChange={(e) => setPassword(e.target.value)}
  134. placeholder="输入密码"
  135. className="w-full pl-9 pr-4 py-2.5 rounded-xl bg-slate-50 dark:bg-slate-800 border border-black/10 dark:border-white/10 text-slate-900 dark:text-white text-xs focus:outline-none focus:border-slate-800 dark:focus:border-slate-200 transition-colors"
  136. />
  137. </div>
  138. </div>
  139. <button
  140. type="submit"
  141. disabled={loading}
  142. className="w-full mt-2 py-3 rounded-xl bg-slate-900 hover:bg-slate-800 dark:bg-white dark:hover:bg-slate-100 text-white dark:text-slate-900 text-xs font-semibold shadow-md flex items-center justify-center space-x-2 disabled:opacity-50 transition-all cursor-pointer"
  143. >
  144. <span>{loading ? '处理中...' : isLoginTab ? '安全登录' : '立即注册'}</span>
  145. {!loading && <ArrowRight className="w-4 h-4" />}
  146. </button>
  147. </form>
  148. <div className="flex items-center justify-center space-x-1.5 text-[11px] text-slate-400 pt-2">
  149. <ShieldCheck className="w-3.5 h-3.5" />
  150. <span>端到端同步加密与身份认证保驾护航</span>
  151. </div>
  152. </div>
  153. </div>
  154. );
  155. };