index.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import Footer from '@/components/Footer';
  2. import { login } from '@/services/ant-design-pro/api';
  3. import { getFakeCaptcha } from '@/services/ant-design-pro/login';
  4. import { LockOutlined, MobileOutlined, UserOutlined } from '@ant-design/icons';
  5. import {
  6. LoginForm,
  7. ProFormCaptcha,
  8. ProFormCheckbox,
  9. ProFormText,
  10. } from '@ant-design/pro-components';
  11. import { useEmotionCss } from '@ant-design/use-emotion-css';
  12. import { FormattedMessage, Helmet, history, useIntl, useModel } from '@umijs/max';
  13. import { Alert, message } from 'antd';
  14. import React, { useState } from 'react';
  15. import { flushSync } from 'react-dom';
  16. import Settings from '../../../../config/defaultSettings';
  17. const LoginMessage: React.FC<{
  18. content: string;
  19. }> = ({ content }) => {
  20. return (
  21. <Alert
  22. style={{
  23. marginBottom: 24,
  24. }}
  25. message={content}
  26. type="error"
  27. showIcon
  28. />
  29. );
  30. };
  31. const Login: React.FC = () => {
  32. const [userLoginState, setUserLoginState] = useState<API.Result>({});
  33. const [type, setType] = useState<string>('mobile');
  34. const { initialState, setInitialState } = useModel('@@initialState');
  35. const containerClassName = useEmotionCss(() => {
  36. return {
  37. display: 'flex',
  38. flexDirection: 'column',
  39. height: '100vh',
  40. overflow: 'auto',
  41. backgroundImage:
  42. "url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/V-_oS6r-i7wAAAAAAAAAAAAAFl94AQBr')",
  43. backgroundSize: '100% 100%',
  44. };
  45. });
  46. const intl = useIntl();
  47. const fetchUserInfo = async () => {
  48. const userInfo = await initialState?.fetchUserInfo?.();
  49. if (userInfo) {
  50. console.log('userInfo--->', userInfo);
  51. flushSync(() => {
  52. setInitialState((s) => ({
  53. ...s,
  54. currentUser: {
  55. avatar: `https://xsgames.co/randomusers/avatar.php?g=pixel&key=${Math.floor(
  56. Math.random() * 10,
  57. )}`,
  58. ...userInfo,
  59. },
  60. }));
  61. });
  62. }
  63. };
  64. const handleSubmit = async (values: API.LoginParams) => {
  65. try {
  66. const { savePhone, ...value } = values;
  67. // 登录
  68. const msg = await login({ ...value });
  69. if (msg.success) {
  70. localStorage.setItem('Admin-Token', msg?.data?.token);
  71. const defaultLoginSuccessMessage = intl.formatMessage({
  72. id: 'pages.login.success',
  73. defaultMessage: '登录成功!',
  74. });
  75. if (savePhone) {
  76. localStorage.setItem('MOBILE', values.mobile as string);
  77. }
  78. message.success(defaultLoginSuccessMessage);
  79. await fetchUserInfo();
  80. const urlParams = new URL(window.location.href).searchParams;
  81. history.push(urlParams.get('redirect') || '/');
  82. return;
  83. }
  84. // 如果失败去设置用户错误信息
  85. setUserLoginState(msg);
  86. } catch (error) {
  87. const defaultLoginFailureMessage = intl.formatMessage({
  88. id: 'pages.login.failure',
  89. defaultMessage: '登录失败,请重试!',
  90. });
  91. console.log(error);
  92. message.error(defaultLoginFailureMessage);
  93. }
  94. };
  95. const { success = true } = userLoginState;
  96. return (
  97. <div className={containerClassName}>
  98. <Helmet>
  99. <title>
  100. {intl.formatMessage({
  101. id: 'menu.login',
  102. defaultMessage: '登录页',
  103. })}
  104. - {Settings.title}
  105. </title>
  106. </Helmet>
  107. <div
  108. style={{
  109. flex: '1',
  110. padding: '32px 0',
  111. }}
  112. >
  113. <LoginForm
  114. contentStyle={{
  115. minWidth: 280,
  116. maxWidth: '75vw',
  117. }}
  118. // logo={<img alt="logo" src="/logo.svg" />}
  119. title="趣程素材库"
  120. subTitle={
  121. <span style={{ display: 'inline-block', height: 16 }}>
  122. {/** intl.formatMessage({ id: 'pages.layouts.userLayout.title' }) */}
  123. </span>
  124. }
  125. initialValues={{
  126. savePhone: true,
  127. mobile: localStorage.getItem('MOBILE') || null,
  128. }}
  129. onFinish={async (values) => {
  130. await handleSubmit(values as API.LoginParams);
  131. }}
  132. submitter={{
  133. searchConfig: {
  134. submitText: '注册/登录',
  135. },
  136. }}
  137. >
  138. {/* <Tabs
  139. activeKey={type}
  140. onChange={setType}
  141. centered
  142. items={[
  143. {
  144. key: 'account',
  145. label: '账户密码登录',
  146. },
  147. {
  148. key: 'mobile',
  149. label: '手机号登录',
  150. },
  151. ]}
  152. /> */}
  153. {!success && type === 'account' && <LoginMessage content={'账户或密码错误'} />}
  154. {type === 'account' && (
  155. <>
  156. <ProFormText
  157. name="username"
  158. fieldProps={{
  159. size: 'large',
  160. prefix: <UserOutlined />,
  161. }}
  162. placeholder={intl.formatMessage({
  163. id: 'pages.login.username.placeholder',
  164. defaultMessage: '用户名: admin or user',
  165. })}
  166. rules={[
  167. {
  168. required: true,
  169. message: (
  170. <FormattedMessage
  171. id="pages.login.username.required"
  172. defaultMessage="请输入用户名!"
  173. />
  174. ),
  175. },
  176. ]}
  177. />
  178. <ProFormText.Password
  179. name="password"
  180. fieldProps={{
  181. size: 'large',
  182. prefix: <LockOutlined />,
  183. }}
  184. placeholder={intl.formatMessage({
  185. id: 'pages.login.password.placeholder',
  186. defaultMessage: '密码: ant.design',
  187. })}
  188. rules={[
  189. {
  190. required: true,
  191. message: (
  192. <FormattedMessage
  193. id="pages.login.password.required"
  194. defaultMessage="请输入密码!"
  195. />
  196. ),
  197. },
  198. ]}
  199. />
  200. </>
  201. )}
  202. {!success && type === 'mobile' && <LoginMessage content="验证码错误" />}
  203. {type === 'mobile' && (
  204. <>
  205. <ProFormText
  206. fieldProps={{
  207. size: 'large',
  208. prefix: <MobileOutlined />,
  209. }}
  210. name="mobile"
  211. placeholder="手机号"
  212. rules={[
  213. {
  214. required: true,
  215. message: '请输入手机号!',
  216. },
  217. {
  218. pattern: /^1\d{10}$/,
  219. message: '手机号格式错误!',
  220. },
  221. ]}
  222. />
  223. <ProFormCaptcha
  224. fieldProps={{
  225. size: 'large',
  226. prefix: <LockOutlined />,
  227. }}
  228. phoneName="mobile"
  229. captchaProps={{
  230. size: 'large',
  231. }}
  232. placeholder="请输入验证码"
  233. captchaTextRender={(timing, count) => {
  234. if (timing) {
  235. return `${count} 获取验证码`;
  236. }
  237. return '获取验证码';
  238. }}
  239. name="code"
  240. rules={[
  241. {
  242. required: true,
  243. message: '请输入验证码!',
  244. },
  245. ]}
  246. onGetCaptcha={async (mobile) => {
  247. const result: any = await getFakeCaptcha({
  248. mobile,
  249. smsType: 'SMS_REG_LOGIN',
  250. });
  251. if (!result?.success) {
  252. throw new Error();
  253. }
  254. message.success('获取验证码成功!');
  255. }}
  256. />
  257. </>
  258. )}
  259. <div style={{ marginBottom: 24 }}>
  260. <ProFormCheckbox noStyle name="savePhone">
  261. 保存手机号
  262. </ProFormCheckbox>
  263. </div>
  264. </LoginForm>
  265. </div>
  266. <Footer />
  267. </div>
  268. );
  269. };
  270. export default Login;