如何使用react 18 + antd (v5.3.0) 实现用户登录功能及用户权限校验?

作为一个企业内部系统,在用户还没有登录的前提下,是不允许访问系统中任何页面内容的。

所以本节要实现的:

  • 实现一个登录页面,在用户没有登录时访问系统只能看到这个登录页面的内容
  • 对于之前内容进行角色和权限判断

另外为了避免刷新页面后登录状态重置,还要保存一下用户的登录信息。

这需要用到浏览器的 localstorage 功能

本文只是提供一种思路,具体,可以根据项目进行自行修改。

Dropdown下拉菜单

如何实现触发事件?

点击菜单项后会触发事件,用户可以通过相应的菜单项 key 进行不同的操作。

import { DownOutlined } from '@ant-design/icons';
import { Dropdown, message, Space } from 'antd';

// 注意此处
const onClick = ({ key }) => {
  message.info(`Click on item ${key}`);
};

const items = [
  {
    label: '1st menu item',
    key: '1',
  },
  {
    label: '2nd menu item',
    key: '2',
  },
  {
    label: '3rd menu item',
    key: '3',
  },
];
const App = () => (
  
     e.preventDefault()}>
      
        Hover me, Click menu item
        
      
    
  
);
export default App;

跳转到登录

如何使用react 18 + antd (v5.3.0) 实现用户登录功能及用户权限校验?_第1张图片

实现代码

import {  useNavigate } from "react-router-dom";

const navigate = useNavigate();  

const onClick = ({ key }) => {  
  if(key.toString() === '4'){
    navigate('/login');
  }
};


布局登录页面

登录框

普通的登录框,可以容纳更多的元素。

实现代码

import { LockOutlined, UserOutlined } from "@ant-design/icons";
import { Button, Form, Input, Space } from "antd";

import styles from "./index.module.css"; 

export default function Login() {
  const onFinish = (values) => {
    console.log("Received values of form: ");
    console.log(values);
  };

  return (
    

后台系统

} placeholder="请输入用户名" /> } type="password" placeholder="请输入密码" /> 注册
); }

页面展示

如何使用react 18 + antd (v5.3.0) 实现用户登录功能及用户权限校验?_第2张图片

登录校验

reactjs-localstorage react存储本地数据,下载地址

安装

npm install reactjs-localstorage
or 
yarn add reactjs-localstorage

使用案例

import {reactLocalStorage} from 'reactjs-localstorage';
 
reactLocalStorage.set('var', true);
reactLocalStorage.get('var', true);
reactLocalStorage.setObject('var', {'test': 'test'});
reactLocalStorage.getObject('var');
reactLocalStorage.remove('var');
reactLocalStorage.clear();

jsonserver由于受限,所以,暂时使用get请求。但是真实环境,一定是后端判断是否可以登录

实现代码

import {reactLocalStorage} from 'reactjs-localstorage'; //引入组件 'reactjs-localstorage'

const onFinish = async (values) => { 
  let userData = await fetchGetUsersLogin(values) 

  // 判断是否是可用用户
  if(userData.length > 0){ 
    reactLocalStorage.setObject('token', userData[0]);
    navigate('/home');
  } else {
    message.error(`登录失败`);
  }
}; 

页面展示

在实际中,后端返回的token往往是一大串大串的字符串,而不是真实用户数据。

如何使用react 18 + antd (v5.3.0) 实现用户登录功能及用户权限校验?_第3张图片

全部代码

import { LockOutlined, UserOutlined } from "@ant-design/icons";
import { Button, Form, Input, Space ,message} from "antd";
import {  useNavigate } from "react-router-dom";

import styles from "./index.module.css";

import { fetchGetUsersLogin } from "../../utils/api";

import {reactLocalStorage} from 'reactjs-localstorage';

export default function Login() {
  const navigate = useNavigate(); 

  const onFinish = async (values) => { 
    let userData = await fetchGetUsersLogin(values) 

    // 判断是否是可用用户
    if(userData.length > 0){ 
      delete  userData[0].password
      reactLocalStorage.setObject('token', userData[0]);
      navigate('/home');
    } else {
      message.error(`登录失败`);
    }
  };

  return (
    

后台系统

} placeholder="请输入用户名" /> } type="password" placeholder="请输入密码" /> 注册
); }

权限校验

此部分,需要修改之前的左侧导航组件:需要根据用户的权限,动态显示左侧导航内容。

详见左侧导航组件内容

修改之前组件,添加用户权限判断

import {reactLocalStorage} from 'reactjs-localstorage';

// 获取用户权限列表
const {role: {rights}} = reactLocalStorage.getObject('token')

const checkPagePermission = (item)=>{
  return  item.key !== "/login"  && rights.includes(item.key) && item.pagepermisson === 1
}
  

全部代码如下:

import { useEffect, useState } from "react";
import { Layout, Menu } from "antd"; 
import { matchRoutes,   useLocation,  useNavigate } from "react-router-dom";

import { routers } from "../../router"; 
import styles from "./SideMenu.module.css"; 
import siteBaseConfig from "../../config"; 
import { fetchGetMenus } from "../../utils/api"; 
import { iconList } from "./iconList";   
import {reactLocalStorage} from 'reactjs-localstorage';

const { Sider } = Layout;

export default function AppLayout() {
  const location = useLocation();
  const navigate = useNavigate(); 

  const [isInit, setIsInit] = useState(false);
  const [collapsed, setCollapsed] = useState(false); 

  // items 菜单内容	ItemType[]
  const [items, setitems] = useState([]); 
  // defaultSelectedKeys 初始选中的菜单项 key 数组
  const [defaultSelectedKeys, setDefaultSelectedKeys] = useState([]); 
  // defaultOpenKeys 初始展开的 SubMenu 菜单项 key 数组
  const [defaultOpenKeys, setDefaultOpenKeys] = useState([]);
  // submenu keys of first level
  const [rootSubmenuKeys , setrootSubmenuKeys ] = useState([]) 
  // openKeys 当前展开的 SubMenu 菜单项 key 数组
  const [openKeys, setOpenKeys] = useState([]);

  // 获取用户权限列表
  const {role: {rights}} = reactLocalStorage.getObject('token')

  const checkPagePermission = (item)=>{
    return  item.key !== "/login"  && rights.includes(item.key) &&
    item.pagepermisson === 1
  }
  
  useEffect(() => {
    async function fetchData() {
      const menusListData = await fetchGetMenus(); 
      let tempItems = [],
      rootSubmenuKeys  = [];  // submenu keys of first level
      menusListData.forEach((item) => {  
        item.key !== "/login" && rootSubmenuKeys.push(item.key) 
        checkPagePermission(item) && tempItems.push({
            label: item.title,
            key: item.key,
            icon: iconList[item.key],
            children:
              item.children &&
              item.children.length > 0 &&
              item.children.map((child) => {
                if (child.pagepermisson === 1) {
                  return {
                    label: child.title,
                    key: child.key,
                    icon: iconList[item.key],
                    children:
                      child.children &&
                      child.children.length > 0 &&
                      child.children.map((sun) => {
                        if (child.pagepermisson === 1) {
                          return {
                            label: sun.title,
                            key: sun.key,
                            icon: iconList[item.key],
                          };
                        }
                      }),
                  };
                }
              }),
          });
      });

      setitems(tempItems); 
      setrootSubmenuKeys(rootSubmenuKeys) 
    }

    fetchData();
  }, []);

  useEffect(() => {
    const routes = matchRoutes(routers, location.pathname); // 返回匹配到的路由数组对象,每一个对象都是一个路由对象
    const pathArr = [];
    if (routes !== null) {
      routes.forEach((item) => {
        const path = item.pathname;
        if (path) {
          pathArr.push(path);
        }
      });
    }
    setDefaultSelectedKeys(pathArr);
    setDefaultOpenKeys(pathArr);
    setIsInit(true);
  }, [location.pathname]);

  if (!isInit) {
    return null;
  }

  const onClick = (e) => { 
    navigate(e.key);
  };
  
  const onOpenChange = (keys) => { 
    const latestOpenKey = keys.find((key) => openKeys.indexOf(key) === -1); 
    
    if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
      setOpenKeys(keys);
    } else {
      setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
    }
  };

  return (
    <>   
      
        
{siteBaseConfig.siteName}
); }

页面展示 – 根据不同用户,显示不同左侧导航

如何使用react 18 + antd (v5.3.0) 实现用户登录功能及用户权限校验?_第4张图片

如何使用react 18 + antd (v5.3.0) 实现用户登录功能及用户权限校验?_第5张图片

用户列表管理优化

问题所在

如何使用react 18 + antd (v5.3.0) 实现用户登录功能及用户权限校验?_第6张图片

解决方案

优化用户列表,在真实环境中,是需要后端返回的

import { reactLocalStorage } from "reactjs-localstorage";

// 获取用户权限列表
  const { roleId, region,username } = reactLocalStorage.getObject("token");

  const roleInterface = {
    "1":"superadmin",
    "2":"regionadmin",
    "3":"regionediter", 
  }

  // 获取用户数据处理
  useEffect(() => {
    const fetchGetUserListHandle = async () => {
      const urseListData = await fetchGetUserList();

      // 注意此处
      setdataSource(roleInterface[roleId] === 'superadmin' ? urseListData: [
        ...urseListData.filter(item => item.username === username),
        ...urseListData.filter(item => item.region === region && roleInterface[item.roleId] === "regionediter"),
      ]);
    };
    fetchGetUserListHandle();
  }, []);

问题解决

如何使用react 18 + antd (v5.3.0) 实现用户登录功能及用户权限校验?_第7张图片

全部代码

import React, { useEffect, useRef, useState } from "react";
import { Space, Switch, Table, Modal, Tag, Button } from "antd";
import {
  DeleteOutlined,
  EditOutlined,
  ExclamationCircleFilled,
} from "@ant-design/icons";

import AddUserModal from "./AddUserForm";
import UpdateUserModal from "./UpdateUserModal";

import {
  fetchGetUserList,
  fetchGetRegionList,
  fetchGetRoles,
  fetchAddUser,
  fetchDeleteUser,
  fetchPatchUser,
} from "../../utils/api";

import { reactLocalStorage } from "reactjs-localstorage";

const { confirm } = Modal;

export default function UserList() {
  const [dataSource, setdataSource] = useState([]);

  // 获取用户权限列表
  const { roleId, region,username } = reactLocalStorage.getObject("token");

  const roleInterface = {
    "1":"superadmin",
    "2":"regionadmin",
    "3":"regionediter", 
  }

  // 获取用户数据处理
  useEffect(() => {
    const fetchGetUserListHandle = async () => {
      const urseListData = await fetchGetUserList();

      // 注意此处
      setdataSource(roleInterface[roleId] === 'superadmin' ? urseListData: [
        ...urseListData.filter(item => item.username === username),
        ...urseListData.filter(item => item.region === region && roleInterface[item.roleId] === "regionediter"),
      ]);
    };
    fetchGetUserListHandle();
  }, []);

  // 添加用户处理
  const [open, setOpen] = useState(false);
  const [regionList, setregionList] = useState([]);
  const [rolesList, serolesList] = useState([]);

  const AddUserFormData = useRef(null);
  // 获取用户数据处理
  useEffect(() => {
    // 获取用户数据处理
    const fetchGetRegionListHandle = async () => {
      const urseListData = await fetchGetRegionList();
      setregionList(urseListData);
    };
    fetchGetRegionListHandle();

    // 获取用户数据处理
    const fetchGetRolesListHandle = async () => {
      const urseListData = await fetchGetRoles();
      serolesList(urseListData);
    };
    fetchGetRolesListHandle();
  }, []);

  const onCreate = async (values) => {
    console.log("Received values of form: ", values);
    setOpen(false);
    //post到后端,生成id,再设置 datasource, 方便后面的删除和更新

    const data = await fetchAddUser({
      ...values,
      roleState: true,
      default: false,
    });

    setdataSource([
      ...dataSource,
      {
        ...data,
        role: rolesList.filter((item) => item.id === values.roleId)[0],
      },
    ]);
  };

  // 删除弹框事件
  const confirmHandel = (item) => {
    confirm({
      title: "您确定要删除吗?",
      icon: ,
      content: "此处删除会删除用户,请谨慎操作!",
      okText: "确认",
      cancelText: "取消",
      onOk() {
        console.log("OK");
        deleteRolesMethod(item);
      },
      onCancel() {
        console.log("Cancel");
      },
    });
  };

  // 删除事件
  const deleteRolesMethod = async (item) => {
    console.log(item);
    setdataSource(dataSource.filter((data) => data.id != item.id));
    const data = await fetchDeleteUser(item.id);
  };

  // 用户状态修改
  const handelChange = async (item) => {
    item.roleState = !item.roleState;
    setdataSource([...dataSource]);

    // 发送请求到后端
    await fetchPatchUser(item.id, { roleState: item.roleState });
  };

  // 用户信息更改
  const [openUpdateModal, setopenUpdateModal] = useState(false);
  const UpdateUserFormData = useRef(null);
  const [isRegionDisable, setisRegionDisable] = useState(false);
  const [curentUserData, setcurentUserData] = useState(null);

  const showUserModal = (item) => {
    console.log(item);
    setopenUpdateModal(true);
    setTimeout(() => {
      if (item.roleId === 1) {
        // 禁用
        setisRegionDisable(true);
      } else {
        // 取消禁用
        setisRegionDisable(false);
      }
      UpdateUserFormData.current.setFieldsValue(item);
      setcurentUserData(item);
    }, 10);
  };

  const onUpdataUserConfirm = async (values) => {
    setopenUpdateModal(false);

    setdataSource(
      dataSource.map((item) => {
        if (item.id === curentUserData.id) {
          return {
            ...item,
            ...values,
            role: rolesList.filter((item) => item.id === values.roleId)[0],
          };
        }
        return item;
      })
    );
    setisRegionDisable(!isRegionDisable);
    // 发送请求到后端
    await fetchPatchUser(curentUserData.id, values);
  };

  const columns = [
    {
      title: "部门",
      dataIndex: "region",
      render: (region) => {
        return region === "" ? "总部" : region;
      },
      filters: [
        ...regionList.map((item) => ({
          text: item.title,
          value: item.value,
        })),
        { text: "超级管理员", value: "超级管理员" },
      ],

      // 用户删选
      onFilter: (value, record) => {
        if (value === "超级管理员") {
          return record.region === "";
        } else {
          return record.region === value;
        }
      },
    },
    {
      title: "角色名称",
      dataIndex: "role",
      render: (role) => {
        return {role?.roleName};
      },
    },

    {
      title: "用户名",
      dataIndex: "username",
    },
    {
      title: "用户可用状态",
      dataIndex: "roleState",
      render: (roleState, item) => {
        // 注意这里
        return (
          
handelChange(item)} />
); }, }, { title: "操作", render: (item) => { return ( ); }, }, ]; return (
item.id} pagination={{ pageSize: 5, }} >
{ setOpen(false); }} regionList={regionList} rolesList={rolesList} ref={AddUserFormData} > { setopenUpdateModal(false); }} regionList={regionList} rolesList={rolesList} ref={UpdateUserFormData} isRegionDisable={isRegionDisable} >
); }

你可能感兴趣的:(前端杂货铺,react.js,前端,javascript)