Login.tsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. // entrypoints/popup/pages/Login.tsx
  2. import React, { useState, useEffect, useRef } from 'react'; // 添加 useRef
  3. import { useNavigate } from 'react-router-dom';
  4. import { Input, Loading, Form, Grid } from '@alifd/next';
  5. const { Row, Col } = Grid
  6. import { CustomMessage } from '@/components/CustomMessage'
  7. import { useLoading } from '@/components/LoadingContext';
  8. import request from '@/components/utils/request'
  9. export default function Login({ setIsLoggedIn }: { setIsLoggedIn: (loggedIn: boolean) => void }) {
  10. const formRef = useRef<any>(null);
  11. const [username, setUsername] = useState('');
  12. const [password, setPassword] = useState('');
  13. const [invitationCode, setInvitationCode] = useState('');
  14. const [loading, setLoading] = useState(false);
  15. const [errors, setErrors] = useState<{ username?: string, password?: string, code?: string, invitationCode?: string }>({});
  16. const [keys, setKeys] = useState('');
  17. const [validImg, setValidImg] = useState('');
  18. const { showLoading, hideLoading } = useLoading();
  19. const navigate = useNavigate();
  20. // 添加验证码状态
  21. const [code, setCode] = useState('');
  22. useEffect(() => {
  23. evVerify()
  24. }, [])
  25. // 添加键盘事件监听
  26. useEffect(() => {
  27. const handleKeyDown = (e: KeyboardEvent) => {
  28. if (e.key === 'Enter' && !loading) {
  29. handleLogin({ username, password, code, invitationCode });
  30. }
  31. };
  32. document.addEventListener('keydown', handleKeyDown);
  33. return () => {
  34. document.removeEventListener('keydown', handleKeyDown);
  35. };
  36. // 添加依赖项,确保每次状态更新时都重新绑定事件监听器
  37. }, [username, password, code, invitationCode, loading]);
  38. const validateForm = () => {
  39. const newErrors: { username?: string, password?: string, code?: string, invitationCode?: string } = {};
  40. if (!username.trim()) {
  41. newErrors.username = '请输入用户名';
  42. }
  43. if (!password) {
  44. newErrors.password = '请输入密码';
  45. } else if (password.length < 6) {
  46. newErrors.password = '密码长度至少为6位';
  47. }
  48. // 添加验证码校验
  49. if (!code.trim()) {
  50. newErrors.code = '请输入验证码';
  51. }
  52. // 添加邀请码校验
  53. // if (!invitationCode.trim()) {
  54. // newErrors.invitationCode = '请输入邀请码';
  55. // }
  56. setErrors(newErrors);
  57. return Object.keys(newErrors).length === 0;
  58. };
  59. // const evVerify = () => {
  60. // fetch('https://user.landwu.com/api/user/verify', {
  61. // method: 'POST',
  62. // headers: {
  63. // 'Content-Type': 'application/json',
  64. // }
  65. // }).then(resjson => {
  66. // return resjson.json();
  67. // }).then(res => {
  68. // const { data = {} } = res;
  69. // const { data: info = {} } = data;
  70. // const { key = "", img = "" } = info;
  71. // setKeys(key)
  72. // setValidImg(img)
  73. // })
  74. // }
  75. const evVerify = async () => {
  76. try {
  77. const res = await request.post('/user/verify');
  78. const { data = {} } = res;
  79. const { data: info = {} } = data;
  80. const { key = "", img = "" } = info;
  81. setKeys(key);
  82. setValidImg(img);
  83. } catch (error) {
  84. CustomMessage.error('获取验证码失败');
  85. }
  86. };
  87. const handleLogin = async (values: any) => {
  88. if (!validateForm()) return;
  89. try {
  90. showLoading('登录中...');
  91. const params = {
  92. ...values,
  93. key: keys
  94. };
  95. const res = await request.post('/user/login', params);
  96. const { msg = "", token = "", code } = res;
  97. if (!code) {
  98. hideLoading();
  99. return false;
  100. }
  101. if (code == -1) {
  102. evVerify();
  103. CustomMessage.error(msg);
  104. hideLoading();
  105. return false;
  106. }
  107. const profileRes = await request.post('/user/getProfile', {api_token: token});
  108. if (profileRes.code == 1) {
  109. const { data = {} } = profileRes;
  110. const { userinfo = {} } = data;
  111. const userinfo_str = JSON.stringify(userinfo);
  112. localStorage.setItem('userinfo', userinfo_str);
  113. CustomMessage.success(msg);
  114. localStorage.setItem("access_token", token);
  115. localStorage.setItem('isLoggedIn', 'true');
  116. setTimeout(() => {
  117. hideLoading();
  118. setIsLoggedIn(true);
  119. navigate('/');
  120. }, 100);
  121. }
  122. } catch (error) {
  123. console.error('登录请求失败:', error);
  124. CustomMessage.error('网络错误,请稍后重试');
  125. hideLoading();
  126. }
  127. };
  128. // const handleLogin = (values: any) => {
  129. // if (!validateForm()) return
  130. // try {
  131. // // setLoading(true);
  132. // showLoading('登录中...');
  133. // const params = {
  134. // ...values,
  135. // key: keys
  136. // }
  137. // // 发送登录请求
  138. // fetch('https://user.landwu.com/api/user/login', {
  139. // method: 'POST',
  140. // headers: {
  141. // 'Content-Type': 'application/json',
  142. // },
  143. // body: JSON.stringify(params),
  144. // }).then(response => response.json()).then(res => {
  145. // const { msg = "", token = "", code } = res;
  146. // if (!code) {
  147. // // setLoading(false);
  148. // hideLoading();
  149. // return false;
  150. // }
  151. // if (code == -1) {
  152. // evVerify()
  153. // CustomMessage.error(msg);
  154. // // setLoading(false);
  155. // hideLoading();
  156. // return false;
  157. // }
  158. // fetch('https://user.landwu.com/api/user/getProfile', {
  159. // method: 'POST',
  160. // headers: {
  161. // 'Content-Type': 'application/json',
  162. // },
  163. // body: JSON.stringify({ api_token: token }),
  164. // }).then(response => response.json()).then(res => {
  165. // if (res.code == 1) {
  166. // const { data = {} } = res;
  167. // const { userinfo = {} } = data;
  168. // const userinfo_str = JSON.stringify(userinfo);
  169. // localStorage.setItem('userinfo', userinfo_str);
  170. // CustomMessage.success(msg);
  171. // localStorage.setItem("access_token", token);
  172. // localStorage.setItem('isLoggedIn', 'true');
  173. // setTimeout(() => {
  174. // // setLoading(false);
  175. // hideLoading();
  176. // setIsLoggedIn(true);
  177. // // 跳转到首页
  178. // navigate('/');
  179. // }, 100)
  180. // }
  181. // }).catch(err => {
  182. // // setLoading(false);
  183. // hideLoading();
  184. // })
  185. // }).catch((e) => {
  186. // console.log("catch")
  187. // // setLoading(false);
  188. // hideLoading();
  189. // });
  190. // } catch (error) {
  191. // console.error('登录请求失败:', error);
  192. // CustomMessage.error('网络错误,请稍后重试');
  193. // hideLoading();
  194. // }
  195. // };
  196. return (
  197. <div className="bg-gray-50 p-4">
  198. <div className="text-center mb-6">
  199. <h1 className="text-2xl font-bold text-gray-800">登录</h1>
  200. </div>
  201. <Form>
  202. <Form.Item
  203. label="用户名"
  204. name="username"
  205. required
  206. help={errors.username}
  207. validateState={errors.username ? 'error' : undefined}
  208. >
  209. <Input
  210. placeholder="请输入用户名"
  211. value={username}
  212. onChange={(value) => {
  213. if (typeof value === 'string') {
  214. setUsername(value);
  215. } else {
  216. setUsername(String(value));
  217. }
  218. if (errors.username) {
  219. setErrors({ ...errors, username: undefined });
  220. }
  221. }}
  222. disabled={loading}
  223. />
  224. </Form.Item>
  225. <Form.Item
  226. label="密码"
  227. name="password"
  228. required
  229. help={errors.password}
  230. validateState={errors.password ? 'error' : undefined}
  231. >
  232. <Input
  233. htmlType="password"
  234. placeholder="请输入密码"
  235. value={password}
  236. onChange={(value) => {
  237. if (typeof value === 'string') {
  238. setPassword(value);
  239. } else {
  240. setPassword(String(value));
  241. }
  242. if (errors.password) {
  243. setErrors({ ...errors, password: undefined });
  244. }
  245. }}
  246. disabled={loading}
  247. />
  248. </Form.Item>
  249. <Form.Item
  250. label="邀请码"
  251. name="invitationCode"
  252. // required
  253. // help={errors.invitationCode}
  254. // validateState={errors.invitationCode ? 'error' : undefined}
  255. >
  256. <Input
  257. htmlType="invitationCode"
  258. placeholder="请输入邀请码"
  259. value={invitationCode}
  260. onChange={(value) => {
  261. if (typeof value === 'string') {
  262. setInvitationCode(value);
  263. } else {
  264. setInvitationCode(String(value));
  265. }
  266. // if (errors.invitationCode) {
  267. // setErrors({ ...errors, invitationCode: undefined });
  268. // }
  269. }}
  270. disabled={loading}
  271. />
  272. </Form.Item>
  273. <Row>
  274. <Col span={16}>
  275. <Form.Item
  276. label="验证码"
  277. name="code"
  278. required
  279. help={errors.code}
  280. validateState={errors.code ? 'error' : undefined}
  281. >
  282. <Input
  283. size="large"
  284. placeholder="请输入验证码"
  285. value={code}
  286. onChange={(value) => {
  287. if (typeof value === 'string') {
  288. setCode(value);
  289. } else {
  290. setCode(String(value));
  291. }
  292. if (errors.code) {
  293. setErrors({ ...errors, code: undefined });
  294. }
  295. }}
  296. disabled={loading}
  297. />
  298. </Form.Item>
  299. </Col>
  300. <Col span={8}>
  301. <img
  302. src={validImg}
  303. style={{
  304. width: 82,
  305. height: 28,
  306. marginTop: 28,
  307. cursor: 'pointer',
  308. }}
  309. onClick={evVerify}
  310. />
  311. </Col>
  312. </Row>
  313. <Form.Item>
  314. <Form.Submit
  315. type="primary"
  316. validate
  317. loading={loading}
  318. onClick={handleLogin}
  319. className="w-full"
  320. style={{ marginRight: 8 }}
  321. >
  322. {loading ? '登录中...' : '登录'}
  323. </Form.Submit>
  324. </Form.Item>
  325. </Form>
  326. <div className="mt-4 text-center text-sm text-gray-500">
  327. <p>忘记密码?请联系管理员</p>
  328. </div>
  329. </div>
  330. );
  331. }