index.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. import { Button, Checkbox, Divider, message, Spin } from 'antd';
  2. import React, { useEffect, useRef, useState, useMemo } from 'react';
  3. import { Link } from 'umi';
  4. import styles from './index.less';
  5. import Token from '@/utils/token';
  6. import Service from '@/pages/user/Login/service';
  7. import { createForm } from '@formily/core';
  8. import { createSchemaField } from '@formily/react';
  9. import { Form, FormItem, Input, Password, Submit } from '@formily/antd';
  10. import { catchError, filter, mergeMap } from 'rxjs/operators';
  11. import * as ICONS from '@ant-design/icons';
  12. import { useModel } from '@@/plugin-model/useModel';
  13. import SystemConst from '@/utils/const';
  14. import { useIntl } from '@@/plugin-locale/localeExports';
  15. import { SelectLang } from '@@/plugin-locale/SelectLang';
  16. import { isNoCommunity } from '@/utils/util';
  17. const Login: React.FC = () => {
  18. const [captcha, setCaptcha] = useState<{ key?: string; base64?: string }>({});
  19. const [bindings, setBindings] = useState<any>([]);
  20. const [basis, setBasis] = useState<any>({});
  21. const { initialState, setInitialState } = useModel('@@initialState');
  22. const intl = useIntl();
  23. const iconMap = new Map();
  24. iconMap.set('dingtalk-ent-app', require('/public/images/bind/dingtalk.png'));
  25. iconMap.set('wechat-webapp', require('/public/images/bind/wechat-webapp.png'));
  26. const defaultImg = require('/public/images/apply/provider1.png');
  27. const viewLogo = require('/public/images/view-logo.png');
  28. const fetchUserInfo = async () => {
  29. const userInfo = (await initialState?.fetchUserInfo?.()) as UserInfo;
  30. if (userInfo) {
  31. await setInitialState({
  32. ...initialState,
  33. currentUser: userInfo,
  34. });
  35. return userInfo;
  36. }
  37. return null;
  38. };
  39. const loginRef = useRef<Partial<LoginParam>>({});
  40. const loginForm = useMemo(
  41. () =>
  42. createForm({
  43. validateFirst: true,
  44. initialValues: loginRef.current,
  45. }),
  46. [captcha],
  47. );
  48. const [loading, setLoading] = useState<boolean>(false);
  49. /** 此方法会跳转到 redirect 参数所在的位置 */
  50. // const goto = () => {
  51. // setTimeout(() => {
  52. // // history.push(redirect || '/');
  53. // // 用于触发app中的render,生成路由
  54. // window.location.href = '/';
  55. // setLoading(false);
  56. // }, 10);
  57. // };
  58. const getCode = () => {
  59. delete loginForm.values?.verifyCode;
  60. loginRef.current = loginForm.values;
  61. // setLoading(false);
  62. Service.captchaConfig()
  63. .pipe(
  64. filter((r) => {
  65. if (!r.enabled) {
  66. setLoading(false);
  67. }
  68. return r.enabled;
  69. }),
  70. mergeMap(Service.getCaptcha),
  71. catchError(() => message.error('系统开小差,请稍后重试')),
  72. )
  73. .subscribe((res) => {
  74. setCaptcha(res);
  75. setLoading(false);
  76. });
  77. };
  78. useEffect(getCode, []);
  79. useEffect(() => {
  80. console.log('版本:' + localStorage.getItem(SystemConst.Version_Code));
  81. localStorage.clear();
  82. Service.getSystemVersion().then((resp) => {
  83. if (resp && resp.status === 200 && resp.result) {
  84. localStorage.setItem(SystemConst.Version_Code, resp.result.edition);
  85. if (resp.result.edition !== 'community') {
  86. Service.bindInfo().then((res) => {
  87. if (res.status === 200) {
  88. setBindings(res.result);
  89. }
  90. });
  91. }
  92. }
  93. });
  94. Service.settingDetail('front').then((res) => {
  95. if (res.status === 200) {
  96. const ico: any = document.querySelector('link[rel="icon"]');
  97. ico.href = res.result.ico;
  98. setBasis(res.result);
  99. // console.log(res.result);
  100. if (res.result.title) {
  101. document.title = res.result.title;
  102. } else {
  103. document.title = '';
  104. }
  105. }
  106. });
  107. //备案配置
  108. // Service.detail(['record']).then((res) => {
  109. // if (res.status === 200) {
  110. // console.log(res.result);
  111. // const item = res.result?.filter((item: any) => item.scope === 'record');
  112. // setRecord(item?.[0].properties.record);
  113. // }
  114. // })
  115. // Service.settingDetail('amap').then((res) => {
  116. // if (res.status === 200) {
  117. // console.log(res.result);
  118. // setRecord(res.result)
  119. // }
  120. // });
  121. }, []);
  122. const SchemaField = createSchemaField({
  123. components: {
  124. FormItem,
  125. Input,
  126. Password,
  127. // Checkbox,
  128. },
  129. scope: {
  130. icon(name: any) {
  131. return React.createElement(ICONS[name]);
  132. },
  133. },
  134. });
  135. const schema = {
  136. type: 'object',
  137. properties: {
  138. username: {
  139. type: 'string',
  140. 'x-decorator': 'FormItem',
  141. 'x-validator': { required: true, message: '请输入用户名!' },
  142. 'x-component': 'Input',
  143. 'x-component-props': {
  144. placeholder: intl.formatMessage({
  145. id: 'pages.login.username.placeholder',
  146. defaultMessage: '用户名',
  147. }),
  148. prefix: "{{icon('UserOutlined')}}",
  149. },
  150. },
  151. password: {
  152. type: 'string',
  153. 'x-validator': { required: true, message: '请输入密码!' },
  154. 'x-decorator': 'FormItem',
  155. 'x-component': 'Password',
  156. 'x-component-props': {
  157. prefix: "{{icon('LockOutlined')}}",
  158. placeholder: intl.formatMessage({
  159. id: 'pages.login.password.placeholder',
  160. defaultMessage: '密码',
  161. }),
  162. },
  163. },
  164. verifyCode: {
  165. type: 'string',
  166. 'x-visible': !!captcha.key,
  167. 'x-decorator': 'FormItem',
  168. 'x-component': 'Input',
  169. 'x-component-props': {
  170. addonAfter: <img src={captcha.base64} alt="验证码" onClick={getCode} />,
  171. placeholder: intl.formatMessage({
  172. id: 'pages.login.captcha.placeholder',
  173. defaultMessage: '请输入验证码',
  174. }),
  175. },
  176. },
  177. },
  178. };
  179. const doLogin = async (data: LoginParam) => {
  180. setLoading(true);
  181. const res = await Service.login2({
  182. expires: loginRef.current.expires,
  183. verifyKey: captcha.key,
  184. ...data,
  185. });
  186. setLoading(false);
  187. if (res.status === 200) {
  188. const userInfo = res.result;
  189. Token.set(userInfo.token);
  190. const userRef: any = await fetchUserInfo();
  191. if (userRef?.user?.username === 'admin') {
  192. const initRef = await Service.initPage();
  193. if (initRef.status === 200 && !initRef.result.length) {
  194. window.location.href = '/#/init-home';
  195. return;
  196. }
  197. }
  198. window.location.href = '/';
  199. } else {
  200. getCode();
  201. setLoading(false);
  202. }
  203. // Service.login({ expires: loginRef.current.expires, verifyKey: captcha.key, ...data }).subscribe(
  204. // {
  205. // next: async (userInfo) => {
  206. // Token.set(userInfo.token);
  207. // const userRef: any = await fetchUserInfo();
  208. // if (userRef?.user?.username === 'admin') {
  209. // const initRef = await Service.initPage()
  210. // if (initRef.status === 200 && !initRef.result.length) {
  211. // window.location.href = '/#/init-home';
  212. // setLoading(false);
  213. // return
  214. // }
  215. // }
  216. // // goto();
  217. // window.location.href = '/';
  218. // setLoading(false);
  219. // },
  220. // error: () => {
  221. // message.error(
  222. // intl.formatMessage({
  223. // id: 'pages.login.failure',
  224. // defaultMessage: '登录失败,请重试!',
  225. // }),
  226. // );
  227. // getCode();
  228. // // setLoading(false);
  229. // },
  230. // complete: () => {
  231. // // getCode();
  232. // // setLoading(false);
  233. // },
  234. // },
  235. // );
  236. };
  237. return (
  238. <Spin spinning={loading} delay={500}>
  239. <div className={styles.container}>
  240. <div className={styles.left}>
  241. <div className={styles.lang} data-lang="">
  242. {SelectLang && <SelectLang />}
  243. </div>
  244. <div className={styles.content}>
  245. <div className={styles.top}>
  246. <div className={styles.header}>
  247. <Link to="/">
  248. <img alt="logo" className={styles.logo} src={basis.logo || '/logo.svg'} />
  249. </Link>
  250. </div>
  251. <div className={styles.desc}>{basis.title || SystemConst.SYSTEM_NAME}</div>
  252. <div className={styles.main}>
  253. <Form form={loginForm} layout="horizontal" size="large" onAutoSubmit={doLogin}>
  254. <SchemaField schema={schema} />
  255. <div className={styles.remember}>
  256. <Checkbox
  257. onChange={(e) => {
  258. loginRef.current.expires = e.target.checked ? -1 : 3600000;
  259. }}
  260. >
  261. 记住我
  262. </Checkbox>
  263. </div>
  264. <Submit block size="large">
  265. {intl.formatMessage({
  266. id: 'pages.login.submit',
  267. defaultMessage: '登录',
  268. })}
  269. </Submit>
  270. {isNoCommunity && (
  271. <div style={{ marginTop: 20 }}>
  272. <Divider plain style={{ height: 12 }}>
  273. <div style={{ color: '#807676d9', fontSize: 12 }}>其他方式登录</div>
  274. </Divider>
  275. <div style={{ position: 'relative', bottom: '10px' }}>
  276. {bindings.map((item: any) => (
  277. <Button
  278. type="link"
  279. onClick={() => {
  280. localStorage.setItem('onLogin', 'no');
  281. // window.open(`/#/account/center/bind`);
  282. window.open(
  283. `/${SystemConst.API_BASE}/application/sso/${item.id}/login`,
  284. );
  285. window.onstorage = (e) => {
  286. if (e.newValue) {
  287. window.location.href = '/';
  288. }
  289. };
  290. }}
  291. >
  292. <img
  293. style={{ width: 32, height: 33 }}
  294. alt={item.name}
  295. src={iconMap.get(item.provider) || defaultImg}
  296. />
  297. </Button>
  298. ))}
  299. </div>
  300. </div>
  301. )}
  302. </Form>
  303. </div>
  304. </div>
  305. </div>
  306. {basis.recommend ? (
  307. <div className={styles.bottom}>
  308. <div className={styles.view}>JETLINKS团队全新力作可视化大屏系统</div>
  309. <div className={styles.url}>
  310. <div style={{ height: 33 }}>
  311. <img src={viewLogo} />
  312. </div>
  313. <a href={'https://view.jetlinks.cn/'} target="_blank" rel="noopener noreferrer">
  314. 体验DEMO
  315. </a>
  316. </div>
  317. </div>
  318. ) : (
  319. <div style={{ height: '8%' }}></div>
  320. )}
  321. </div>
  322. <div className={styles.right}>
  323. <img
  324. src={basis.backgroud || require('/public/images/login.png')}
  325. style={{ width: '100%', height: '100%' }}
  326. />
  327. {basis.record && (
  328. <a
  329. href={'https://beian.miit.gov.cn/#/Integrated/index'}
  330. target="_blank"
  331. rel="noopener noreferrer"
  332. className={styles.records}
  333. >
  334. {basis.record}
  335. </a>
  336. )}
  337. </div>
  338. </div>
  339. </Spin>
  340. );
  341. };
  342. export default Login;