index.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import { useState, useEffect } from "react";
  2. import {
  3. Box,
  4. Flex,
  5. TextInput,
  6. Button,
  7. FormControl,
  8. Note,
  9. IconButton,
  10. } from "@contentful/f36-components";
  11. import { EyeIcon, EyeClosedIcon } from "@contentful/f36-icons";
  12. import { useNavigate } from "react-router-dom";
  13. import { authApi } from "@/services/modules/system";
  14. import { useAuth } from "@/contexts/AuthContext";
  15. import { useLoginAuth } from "./hooks/useAuth";
  16. import { debounce } from "@/utils/lodash";
  17. import styles from "./index.module.css";
  18. export default function LoginPage() {
  19. const [account, setAccount] = useState("内容中心管理员");
  20. const [password, setPassword] = useState("zyh931015$");
  21. const [captcha, setCaptcha] = useState("");
  22. const [captchaKey, setCaptchaKey] = useState("");
  23. const [captchaImage, setCaptchaImage] = useState("");
  24. const [errorMessage, setErrorMessage] = useState("");
  25. const [showPassword, setShowPassword] = useState(false);
  26. const navigate = useNavigate();
  27. const { refreshAuth } = useAuth();
  28. const { login, loading } = useLoginAuth();
  29. // 获取验证码
  30. const fetchCaptcha = async () => {
  31. const checkKey =
  32. new Date().getTime() + Math.random().toString(36).slice(-4); // 1629428467008;
  33. setCaptchaKey(checkKey);
  34. try {
  35. const result = await authApi.getCaptcha(checkKey);
  36. setCaptchaImage(result);
  37. setCaptcha(""); // 清空验证码输入
  38. } catch (error) {
  39. console.error("获取验证码失败:", error);
  40. }
  41. };
  42. // 页面加载时获取验证码
  43. useEffect(() => {
  44. fetchCaptcha();
  45. }, []);
  46. const handleLogin = async (e: React.FormEvent) => {
  47. e.preventDefault();
  48. // 清空之前的错误
  49. setErrorMessage("");
  50. // 验证表单 - 只检查必填
  51. if (!account.trim() || !password.trim() || !captcha.trim()) {
  52. setErrorMessage("账号、密码或验证码不能为空");
  53. return;
  54. }
  55. try {
  56. // 调用登录(使用 hook 统一处理)
  57. await login({
  58. username: account,
  59. password,
  60. captcha,
  61. checkKey: captchaKey,
  62. });
  63. console.log("登录成功");
  64. // 刷新权限数据(加载菜单)
  65. await refreshAuth();
  66. // 跳转到首页
  67. navigate("/content-model");
  68. } catch (error: any) {
  69. console.error("登录失败:", error);
  70. setErrorMessage(error.message || "登录失败,请稍后重试");
  71. // 刷新验证码
  72. fetchCaptcha();
  73. }
  74. };
  75. return (
  76. <Flex
  77. justifyContent="center"
  78. alignItems="center"
  79. className={styles.container}
  80. >
  81. <Box className={styles.loginBox}>
  82. {/* 标题 */}
  83. <Box className={styles.header}>
  84. <h1 className={styles.title}>欢迎回来</h1>
  85. <h2 className={styles.subtitle}>登录你的账号</h2>
  86. </Box>
  87. {/* 登录表单 */}
  88. <Box className={styles.formContainer}>
  89. <form onSubmit={handleLogin}>
  90. <Flex flexDirection="column" gap="spacingS">
  91. {/* 错误提示 */}
  92. {errorMessage && <Note variant="warning">{errorMessage}</Note>}
  93. {/* account */}
  94. <FormControl className={styles.form_control}>
  95. <FormControl.Label>账号</FormControl.Label>
  96. <TextInput
  97. // type="account"
  98. value={account}
  99. onChange={(e) => {
  100. setAccount(e.target.value);
  101. // 清除错误提示
  102. if (errorMessage) {
  103. setErrorMessage("");
  104. }
  105. }}
  106. placeholder="请输入你的账号"
  107. />
  108. </FormControl>
  109. {/* Password */}
  110. <FormControl className={styles.form_control}>
  111. <FormControl.Label>密码</FormControl.Label>
  112. <Box className={styles.passwordInputWrapper}>
  113. <TextInput
  114. type={showPassword ? "text" : "password"}
  115. value={password}
  116. onChange={(e) => {
  117. setPassword(e.target.value);
  118. // 清除错误提示
  119. if (errorMessage) {
  120. setErrorMessage("");
  121. }
  122. }}
  123. placeholder="请输入你的密码"
  124. />
  125. <IconButton
  126. aria-label={showPassword ? "隐藏密码" : "显示密码"}
  127. icon={showPassword ? <EyeClosedIcon/> : <EyeIcon />}
  128. variant="transparent"
  129. size="small"
  130. onClick={() => setShowPassword(!showPassword)}
  131. className={styles.passwordToggle}
  132. style={{
  133. opacity: showPassword ? 0.5 : 1,
  134. transform: showPassword ? 'scale(0.9)' : 'scale(1)'
  135. }}
  136. />
  137. </Box>
  138. </FormControl>
  139. {/* Captcha */}
  140. <FormControl className={styles.form_control}>
  141. <FormControl.Label>验证码</FormControl.Label>
  142. <Flex gap="spacingS" alignItems="center">
  143. <Box className={styles.captchaInputWrapper}>
  144. <TextInput
  145. value={captcha}
  146. onChange={(e) => {
  147. setCaptcha(e.target.value);
  148. // 清除错误提示
  149. if (errorMessage) {
  150. setErrorMessage("");
  151. }
  152. }}
  153. placeholder="请输入验证码"
  154. />
  155. </Box>
  156. <Box
  157. onClick={debounce(fetchCaptcha, 500)}
  158. className={styles.captchaImage}
  159. >
  160. {captchaImage ? (
  161. <img
  162. src={captchaImage}
  163. alt="验证码"
  164. />
  165. ) : (
  166. <span className={styles.captchaPlaceholder}>
  167. 点击刷新
  168. </span>
  169. )}
  170. </Box>
  171. </Flex>
  172. </FormControl>
  173. {/* Remember me */}
  174. {/* <Checkbox
  175. isChecked={rememberMe}
  176. onChange={() => setRememberMe(!rememberMe)}
  177. >
  178. Remember me
  179. </Checkbox> */}
  180. {/* Login Button */}
  181. <Button
  182. variant="primary"
  183. size="large"
  184. type="submit"
  185. isFullWidth
  186. isLoading={loading}
  187. >
  188. Log in
  189. </Button>
  190. </Flex>
  191. </form>
  192. </Box>
  193. {/* Footer Links */}
  194. {/* <Flex
  195. justifyContent="center"
  196. gap="spacingS"
  197. marginTop="spacingL"
  198. className={styles.footer}
  199. >
  200. <Button variant="transparent" size="small">
  201. Log in via SSO
  202. </Button>
  203. <Text>|</Text>
  204. <Button variant="transparent" size="small">
  205. Reset password
  206. </Button>
  207. <Text>|</Text>
  208. <Button variant="transparent" size="small">
  209. Resend confirmation
  210. </Button>
  211. <Text>|</Text>
  212. <Button variant="transparent" size="small">
  213. Unlock account
  214. </Button>
  215. </Flex> */}
  216. </Box>
  217. </Flex>
  218. );
  219. }