Login.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import React, { useRef, useEffect } from 'react';
  2. import { Row, Col, Form, Input, Button, Card, message, Image } from 'antd';
  3. import type { InputRef } from 'antd';
  4. import { useSelector } from 'react-redux';
  5. import { RootState } from '../../states/store';
  6. import { SystemMode } from '../../states/systemModeSlice';
  7. import loginBrand from 'src/assets/imgs/login-brand.jpeg';
  8. import { useDispatch } from 'react-redux';
  9. import {
  10. login as loginApi,
  11. LoginApiResponse,
  12. } from '../../API/security/userActions';
  13. import { setUserInfo, type UserInfoState } from '../../states/user_info'; // 同时导入 setUserInfo 和类型 UserInfoState
  14. import handleEmergencyOperation from '../../domain/patient/handleEmergencyOperation';
  15. import { setSystemMode } from '../../states/systemModeSlice';
  16. const Login: React.FC = () => {
  17. const [form] = Form.useForm();
  18. const dispatch = useDispatch();
  19. const systemMode = useSelector((state: RootState) => state.systemMode.mode);
  20. const userInfo = useSelector((state: RootState) => state.userInfo);
  21. const usernameInputRef = useRef<InputRef>(null);
  22. console.log(`========${systemMode}`);
  23. // 自动聚焦到用户名输入框
  24. useEffect(() => {
  25. if (usernameInputRef.current) {
  26. usernameInputRef.current.focus();
  27. }
  28. }, []);
  29. if (systemMode === SystemMode.Emergency) {
  30. return null;
  31. }
  32. //非急诊,但已经登录,不显示login
  33. if (systemMode === SystemMode.Normal && !!userInfo && userInfo.uid !== 0) {
  34. return null;
  35. }
  36. // 2. 登录请求函数
  37. const loginRequest = async (values: {
  38. username: string;
  39. password: string;
  40. }): Promise<LoginApiResponse> => {
  41. const res = await loginApi(values.username, values.password);
  42. return res.data;
  43. };
  44. // 3. handleFinish 处理流程
  45. const handleFinish = async (values: {
  46. username: string;
  47. password: string;
  48. }): Promise<void> => {
  49. try {
  50. const result = await loginRequest(values);
  51. if (result.code === '0x000000') {
  52. // 4. 转换为 UserInfoState 类型
  53. const userInfo: UserInfoState = {
  54. token: result.data.token,
  55. expire: result.data.expire,
  56. uid: result.data.uid,
  57. name: result.data.name,
  58. avatar: result.data.avatar,
  59. };
  60. // 5. 分发 redux action
  61. dispatch(setUserInfo(userInfo));
  62. dispatch(setSystemMode(SystemMode.Normal));
  63. message.success('登录成功'); //todo 更详细的提示与异步过程
  64. } else {
  65. message.error(`登录失败,详情:${result.description}`);
  66. }
  67. } catch (e) {
  68. message.error(
  69. `登录失败,网络错误: ${e instanceof Error ? e.message : '未知错误'}`
  70. );
  71. }
  72. };
  73. // 急诊操作处理流程
  74. const handleEmergencyClick = async (
  75. e: React.MouseEvent<HTMLElement>
  76. ): Promise<void> => {
  77. e.preventDefault(); // 阻止表单提交
  78. try {
  79. await handleEmergencyOperation();
  80. message.success('急诊模式启动成功');
  81. } catch (e) {
  82. dispatch(setSystemMode(SystemMode.Normal));
  83. message.error(
  84. `急诊操作失败: ${e instanceof Error ? e.message : '未知错误'}`
  85. );
  86. console.error('Emergency operation failed:', e);
  87. }
  88. };
  89. return (
  90. <Row
  91. className="fixed inset-0 z-50 bg-white bg-opacity-100 w-full h-full flex items-center justify-center"
  92. justify="center"
  93. align="middle"
  94. >
  95. <Col
  96. xs={24}
  97. sm={24}
  98. md={14}
  99. lg={12}
  100. xl={12}
  101. className="flex items-center justify-center h-full"
  102. >
  103. <Card className="max-w-md shadow-lg w-[480px] max-auto">
  104. <Form
  105. form={form}
  106. layout="vertical"
  107. onFinish={handleFinish}
  108. className="space-y-6 "
  109. >
  110. <Form.Item
  111. label="用户名"
  112. name="username"
  113. rules={[{ required: true, message: '请输入用户名' }]}
  114. >
  115. <Input
  116. ref={usernameInputRef}
  117. size="large"
  118. placeholder="请输入用户名"
  119. data-testid="login-username-input"
  120. />
  121. </Form.Item>
  122. <Form.Item
  123. label="密码"
  124. name="password"
  125. rules={[{ required: true, message: '请输入密码' }]}
  126. >
  127. <Input.Password
  128. size="large"
  129. placeholder="请输入密码"
  130. className="py-0 px-[11px]"
  131. data-testid="login-password-input"
  132. />
  133. </Form.Item>
  134. <Form.Item>
  135. <Row gutter={16}>
  136. <Col span={12}>
  137. <Button
  138. type="primary"
  139. htmlType="submit"
  140. size="large"
  141. className="w-full"
  142. data-testid="login-submit-button"
  143. >
  144. 登录
  145. </Button>
  146. </Col>
  147. <Col span={12}>
  148. <Button
  149. type="primary"
  150. size="large"
  151. className="w-full"
  152. onClick={handleEmergencyClick}
  153. >
  154. 急诊
  155. </Button>
  156. </Col>
  157. </Row>
  158. </Form.Item>
  159. </Form>
  160. </Card>
  161. </Col>
  162. <Col
  163. xs={0}
  164. sm={0}
  165. md={10}
  166. lg={12}
  167. xl={12}
  168. className="h-full flex items-center justify-center"
  169. >
  170. {/* 右侧区域可用于展示插画或品牌信息,当前留空 */}
  171. <Image
  172. src={loginBrand}
  173. alt="Brand Logo"
  174. className="max-w-full h-auto"
  175. />
  176. </Col>
  177. </Row>
  178. );
  179. };
  180. export default Login;