Tdesign React框架原生代码实现前端菜单权限控制

需求:
登录后不同权限用户显示不同的菜单。

实现思路:
1.后端创建一个接口,用来获取用户权限,例如我的接口根据token请求返回的roleid就是用来进行权限判断的。
2.修改原生的getuserinfo,因为登录接口请求成功后,我会调用getinfo,所以在前端存储中拿到token获取返回的roleid,然后重点:把获取到的roleid存储在前端,否则会导致刷新页面后菜单就失效了。
3.更新Menu.tsx,就可以实现了。因为官网没有React部分的权限控制说明,所以在此记录。

接口请求及数据示例

POST /users/info HTTP/1.1
Host: 10.211.55.3:8080
Origin: http://localhost:3003
Referer: http://localhost:3003/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0
Token: 36afd276-7c21-44f8-8240-3d8434aa5965
Content-Type: application/json

{
}

{
  "uid": 508151339008790528,
  "username": "test4",
  "mobile": "15555555555",
  "roleid": 1,
  "loginname": "15555555555"
}
//  src/modules/user/index.ts
export const getUserInfo = createAsyncThunk(`${namespace}/users/info`, async () => {
  const token = localStorage.getItem(TOKEN_NAME); // Retrieve the token from localStorage
  console.log('Token:', token); // Log the token

  const fetchUserInfo = async (token: string | null) => {
    if (!token) {
      throw new Error('Token is missing');
    }

    try {
      const response = await fetch(`/users/info`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Token': token,
        },
        body: JSON.stringify({}),
      });

      if (!response.ok) {
        throw new Error('Network response was not ok');
      }

      const userInfo = await response.json();
      return userInfo;
    } catch (error) {
      console.error('Error fetching user info:', error);
      throw error;
    }
  };

  const userInfo = await fetchUserInfo(token);

  let roles: string[] = [];
  switch (userInfo.roleid) {
    case 1:
      roles = ['admin'];
      break;
    case 2:
      roles = ['officer'];
      break;
    case 3:
      roles = ['rounder'];
      break;
    default:
      roles = [];
  }

  localStorage.setItem('userRoles', JSON.stringify(roles));

  console.log('Fetched user info:', userInfo); // Log fetched user info
  console.log('Assigned roles:', roles); // Log assigned roles

  return {
    ...userInfo,
    roles,
  };
});

/ login
export const login = createAsyncThunk(`${namespace}/login`, async (userInfo: Record<string, unknown>, { dispatch }) => {
  const loginRequest = async (userInfo: Record<string, unknown>) => {
    try {
      const response = await fetch(`${apiBaseUrl}/login`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(userInfo),
      });

      if (!response.ok) {
        throw new Error('Network response was not ok');
      }

      const res = await response.json();

      if (res.success === true) {
        localStorage.setItem(TOKEN_NAME, res.content); // Store the token from the content property
        await dispatch(getUserInfo());
        return res.content;
      } else {
        throw new Error(res.message || 'Login failed');
      }
    } catch (error) {
      console.error('Error during login:', error);
      throw error;
    }
  };

  const res = await loginRequest(userInfo);
  return res;
});

修改菜单文件

// src/layout/components/Menu.tsx

import React, { memo, useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { Menu, MenuValue } from 'tdesign-react';
import router, { IRouter, filterRoutesByRole } from 'router';
import { resolve } from 'utils/path';
import { useAppSelector } from 'modules/store';
import { selectGlobal } from 'modules/global';
import MenuLogo from './MenuLogo';
import Style from './Menu.module.less';

const { SubMenu, MenuItem, HeadMenu } = Menu;

interface IMenuProps {
  showLogo?: boolean;
  showOperation?: boolean;
}

const renderMenuItems = (menu: IRouter[], parentPath = '') => {
  const navigate = useNavigate();
  return menu.map((item) => {
    const { children, meta, path } = item;

    if (!meta || meta?.hidden === true) {
      return null;
    }

    const { Icon, title, single } = meta;
    const routerPath = resolve(parentPath, path);

    if (!children || children.length === 0) {
      return (
        <MenuItem
          key={routerPath}
          value={routerPath}
          icon={Icon ? <Icon /> : undefined}
          onClick={() => navigate(routerPath)}
        >
          {title}
        </MenuItem>
      );
    }

    if (single && children?.length > 0) {
      const firstChild = children[0];
      if (firstChild?.meta && !firstChild?.meta?.hidden) {
        const { Icon, title } = meta;
        const singlePath = resolve(resolve(parentPath, path), firstChild.path);
        return (
          <MenuItem
            key={singlePath}
            value={singlePath}
            icon={Icon ? <Icon /> : undefined}
            onClick={() => navigate(singlePath)}
          >
            {title}
          </MenuItem>
        );
      }
    }

    return (
      <SubMenu key={routerPath} value={routerPath} title={title} icon={Icon ? <Icon /> : undefined}>
        {renderMenuItems(children, routerPath)}
      </SubMenu>
    );
  });
};

/**
 * 顶部菜单
 */
export const HeaderMenu = memo(() => {
  const globalState = useAppSelector(selectGlobal);
  const location = useLocation();
  const userRoles = useAppSelector((state) => state.user.userInfo.roles);
  const filteredRoutes = filterRoutesByRole(router, userRoles);
  const [active, setActive] = useState<MenuValue>(location.pathname);

  return (
    <HeadMenu
      expandType='popup'
      style={{ marginBottom: 20 }}
      value={active}
      theme={globalState.theme}
      onChange={(v) => setActive(v)}
    >
      {renderMenuItems(filteredRoutes)}
    </HeadMenu>
  );
});

/**
 * 左侧菜单
 */
export default memo((props: IMenuProps) => {
  const location = useLocation();
  const globalState = useAppSelector(selectGlobal);
  const userRoles = useAppSelector((state) => state.user.userInfo.roles);

  console.log('User roles from state:', userRoles); // Log user roles from state

  const filteredRoutes = filterRoutesByRole(router, userRoles);

  const { version } = globalState;
  const bottomText = globalState.collapsed ? version : `阿巴阿巴 ${version}`;

  return (
    <Menu
      width='232px'
      style={{ flexShrink: 0, height: '100%' }}
      className={Style.menuPanel2}
      value={location.pathname}
      theme={globalState.theme}
      collapsed={globalState.collapsed}
      operations={props.showOperation ? <div className={Style.menuTip}>{bottomText}</div> : undefined}
      logo={props.showLogo ? <MenuLogo collapsed={globalState.collapsed} /> : undefined}
    >
      {renderMenuItems(filteredRoutes)}
    </Menu>
  );
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值