Browse Source

添加登录页面,在系统启动时显示登录页面

dengdx 2 months ago
parent
commit
62030a1d19

+ 10 - 0
src/layouts/BasicLayout.tsx

@@ -2,6 +2,8 @@
 import React, { useState } from 'react';
 import React, { useState } from 'react';
 import { Layout, Row, Col, ConfigProvider, Image } from 'antd';
 import { Layout, Row, Col, ConfigProvider, Image } from 'antd';
 import { TabBar } from 'antd-mobile';
 import { TabBar } from 'antd-mobile';
+import { useSelector } from 'react-redux';
+
 import logo from '../assets/imgs/logo-v3.jpg';
 import logo from '../assets/imgs/logo-v3.jpg';
 import NavbarFloat from './NavbarFloat';
 import NavbarFloat from './NavbarFloat';
 import NavMenu from './NavMenu';
 import NavMenu from './NavMenu';
@@ -17,6 +19,9 @@ import OutputlistPage from '@/pages/patient/OutputList';
 import PatientManagement from '@/pages/patient/PatientManagement';
 import PatientManagement from '@/pages/patient/PatientManagement';
 import MeButton from '@/pages/security/components/MeButton';
 import MeButton from '@/pages/security/components/MeButton';
 import Profile from '@/pages/security/Profile';
 import Profile from '@/pages/security/Profile';
+import Login from '@/pages/security/Login';
+
+import type { RootState } from '@/states/store';
 
 
 // import { Link } from 'react-router-dom';
 // import { Link } from 'react-router-dom';
 // import { MenuOutlined } from '@ant-design/icons';
 // import { MenuOutlined } from '@ant-design/icons';
@@ -62,6 +67,9 @@ const BasicLayout: React.FC<BasicLayoutProps> = () => {
   const handleMenuClick = (key: string) => {
   const handleMenuClick = (key: string) => {
     setCurrentKey(key);
     setCurrentKey(key);
   };
   };
+
+  const userInfo = useSelector((state: RootState) => state.userInfo);
+
   return (
   return (
     // ConfigProvider 用于自定义 Ant Design 的主题和 breakpoints
     // ConfigProvider 用于自定义 Ant Design 的主题和 breakpoints
     <ConfigProvider
     <ConfigProvider
@@ -79,6 +87,8 @@ const BasicLayout: React.FC<BasicLayoutProps> = () => {
         }
         }
       }
       }
     >
     >
+      {/* 登录遮罩层 */}
+      {userInfo.id === null && <Login />}
       <Layout
       <Layout
         style={{
         style={{
           minHeight: '100vh',
           minHeight: '100vh',

+ 12 - 8
src/pages/demo/App.tsx

@@ -1,6 +1,8 @@
 // App.tsx
 // App.tsx
 import React from 'react';
 import React from 'react';
 import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
 import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
+import { Provider } from 'react-redux';
+import store from '@/states/store';
 import BasicLayout from '../../layouts/BasicLayout';
 import BasicLayout from '../../layouts/BasicLayout';
 import HomePage from './HomePage';
 import HomePage from './HomePage';
 import AboutPage from './AboutPage';
 import AboutPage from './AboutPage';
@@ -8,14 +10,16 @@ import AboutPage from './AboutPage';
 const App: React.FC = () => {
 const App: React.FC = () => {
   console.log('App component rendered');
   console.log('App component rendered');
   return (
   return (
-    <Router>
-      <BasicLayout>
-        <Routes>
-          <Route path="/" element={<HomePage />} />
-          <Route path="/about" element={<AboutPage />} />
-        </Routes>
-      </BasicLayout>
-    </Router>
+    <Provider store={store}>
+      <Router>
+        <BasicLayout>
+          <Routes>
+            <Route path="/" element={<HomePage />} />
+            <Route path="/about" element={<AboutPage />} />
+          </Routes>
+        </BasicLayout>
+      </Router>
+    </Provider>
   );
   );
 };
 };
 
 

+ 10 - 0
src/pages/security/Login.md

@@ -0,0 +1,10 @@
+# 关于login页面
+
+在主界面上层显示login页面,用于用户登录系统
+
+# 实现
+
+> 布局。24列栅格布局,大屏环境下,第一列占用18列,第二列占据6列;中屏环境下,第一列占用20列,第二列占用4列;小屏环境下,第一列占用24栅格列,第二列不显示;
+> 关于第一列。第一列用于登录表单的容器,表单竖直方向居中显示;
+> 关于表单。自上向下分若干行,第一行是用户名,第二行是密码,第三行是登录按钮
+> 基于ant design组件库。

+ 82 - 0
src/pages/security/Login.tsx

@@ -0,0 +1,82 @@
+import React from 'react';
+import { Row, Col, Form, Input, Button, Card } from 'antd';
+
+const Login: React.FC = () => {
+  const [form] = Form.useForm();
+
+  // type LoginFormValues = {
+  //     username: string;
+  //     password: string;
+  // };
+
+  interface LoginResponse {
+    token: string;
+    expire: string;
+    uid: string;
+    name: string;
+    avatar: string;
+  }
+  const handleFinish = (values: LoginResponse) => {
+    // TODO: 登录逻辑
+    console.log('Login:', values);
+  };
+
+  return (
+    <Row
+      className="fixed inset-0 z-50 bg-white bg-opacity-100 w-full h-full flex items-center justify-center"
+      justify="center"
+      align="middle"
+    >
+      <Col
+        xs={24}
+        sm={24}
+        md={20}
+        lg={18}
+        xl={18}
+        className="flex items-center justify-center h-full"
+      >
+        <Card className="max-w-md shadow-lg w-[480px] max-auto">
+          <Form
+            form={form}
+            layout="vertical"
+            onFinish={handleFinish}
+            className="space-y-6 "
+          >
+            <Form.Item
+              label="用户名"
+              name="username"
+              rules={[{ required: true, message: '请输入用户名' }]}
+            >
+              <Input size="large" placeholder="请输入用户名" />
+            </Form.Item>
+            <Form.Item
+              label="密码"
+              name="password"
+              rules={[{ required: true, message: '请输入密码' }]}
+            >
+              <Input.Password size="large" placeholder="请输入密码" />
+            </Form.Item>
+            <Form.Item>
+              <Button
+                type="primary"
+                htmlType="submit"
+                size="large"
+                className="w-full"
+              >
+                登录
+              </Button>
+            </Form.Item>
+          </Form>
+        </Card>
+      </Col>
+      <Col xs={0} sm={0} md={4} lg={6} xl={6} className="h-full">
+        {/* 右侧区域可用于展示插画或品牌信息,当前留空 */}
+        <div className="h-full flex items-center justify-center">
+          <h2 className="text-gray-500">欢迎使用</h2>
+        </div>
+      </Col>
+    </Row>
+  );
+};
+
+export default Login;

+ 12 - 0
src/states/store.ts

@@ -0,0 +1,12 @@
+import { configureStore } from '@reduxjs/toolkit';
+import userInfoReducer from './user_info';
+
+const store = configureStore({
+  reducer: {
+    userInfo: userInfoReducer,
+  },
+});
+
+export type RootState = ReturnType<typeof store.getState>;
+export type AppDispatch = typeof store.dispatch;
+export default store;

+ 50 - 0
src/states/user_info/index.ts

@@ -0,0 +1,50 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+
+interface UserInfoState {
+  id: string | null;
+  name: string;
+  email: string;
+  avatarUrl?: string;
+}
+
+const initialState: UserInfoState = {
+  id: null,
+  name: '',
+  email: '',
+  avatarUrl: undefined,
+};
+
+const userInfoSlice = createSlice({
+  name: 'userInfo',
+  initialState,
+  reducers: {
+    setUserInfo(state: UserInfoState, action: PayloadAction<UserInfoState>) {
+      return { ...action.payload };
+    },
+    clearUserInfo(state) {
+      state.id = null;
+      state.name = '';
+      state.email = '';
+      state.avatarUrl = undefined;
+    },
+    updateUserName(state, action: PayloadAction<string>) {
+      state.name = action.payload;
+    },
+    updateUserEmail(state, action: PayloadAction<string>) {
+      state.email = action.payload;
+    },
+    updateUserAvatar(state, action: PayloadAction<string | undefined>) {
+      state.avatarUrl = action.payload;
+    },
+  },
+});
+
+export const {
+  setUserInfo,
+  clearUserInfo,
+  updateUserName,
+  updateUserEmail,
+  updateUserAvatar,
+} = userInfoSlice.actions;
+
+export default userInfoSlice.reducer;